# # # patch "cmd_policy.cc" # from [a28b3fb83a3e2393ce0e4043f86c2e3233a553a2] # to [5599e7b4f42f2eef5a2246fc8e24bff25cf93667] # # patch "policies/delegation.cc" # from [4b23ad3d399c881870a36c4fe6ee124b1d8cadb1] # to [eb6d79d94d11c13697ce1e48e2b6c699a5cf2117] # # patch "policies/editable_policy.cc" # from [d9bdbd2e50c5cc7ca087ab9aa54c813ecb423b33] # to [723c66e1b8225aa2a70c3088c463e38b31713d70] # # patch "policies/editable_policy.hh" # from [6c22ef4e400ff2487af125981e6793d4b52f280f] # to [9c71d01df39b3e6b8be7e0d74df7cd1e8173d744] # # patch "policies/policy.hh" # from [167080f8a791cd65a87a08fea69980e14787cae2] # to [7accc7c4f749b975d0dfc1ec1ab427f65d2a6a46] # # patch "policies/policy_branch.cc" # from [740286a7466517294d7660f19e089957ef80d661] # to [d359facea38e2d4e66e455e26ef1ae4e37c873b8] # # patch "policies/policy_branch.hh" # from [473e09b6b2700615b229a868eaa122cd48658947] # to [2cf62bc1c862c968fa2b660796fcd79ddee1bab3] # # patch "project.cc" # from [8b1e22897df1bd5e81ce646873f95b5331b395d3] # to [b6041bfe58f791918414f7e694d52d8ce23c9b3f] # ============================================================ --- cmd_policy.cc a28b3fb83a3e2393ce0e4043f86c2e3233a553a2 +++ cmd_policy.cc 5599e7b4f42f2eef5a2246fc8e24bff25cf93667 @@ -106,7 +106,8 @@ CMD(create_subpolicy, "create_subpolicy" gov.back().policy, gov.back().delegation.get_branch_spec()); - policies::editable_policy parent(*parent_branch.begin()->second); + policies::editable_policy parent; + parent_branch.get_policy(parent, origin::user); std::set admin_keys; { @@ -121,7 +122,7 @@ CMD(create_subpolicy, "create_subpolicy" parent_branch.commit(project, keys, parent, utf8("Add delegation to new child policy"), - parent_branch.begin()); + origin::user); } CMD(create_branch, "create_branch", "", CMD_REF(policy), @@ -147,10 +148,12 @@ CMD(create_branch, "create_branch", "", F("Cannot find policy over '%s'") % branch); E(gov.back().delegation.is_branch_type(), origin::user, F("cannot edit '%s', it is delegated to a specific revision") % branch); + policies::policy_branch parent(project, gov.back().policy, gov.back().delegation.get_branch_spec()); - policies::editable_policy ppol(*parent.begin()->second); + policies::editable_policy ppol; + parent.get_policy(ppol, origin::user); std::set admin_keys; { key_identity_info ident; @@ -165,7 +168,7 @@ CMD(create_branch, "create_branch", "", ppol.set_branch(suffix(), policies::branch::create(app, admin_keys)); parent.commit(project, keys, ppol, utf8("Add branch."), - parent.begin()); + origin::user); } CMD_FWD_DECL(list); ============================================================ --- policies/delegation.cc 4b23ad3d399c881870a36c4fe6ee124b1d8cadb1 +++ policies/delegation.cc eb6d79d94d11c13697ce1e48e2b6c699a5cf2117 @@ -98,18 +98,15 @@ namespace policies { case branch_type: { policy_branch br(project, parent, branch_desc); - if (br.size() != 1) + policy_ptr ret(new policy()); + if (br.try_get_policy(*ret)) + return ret; + else { - W(F("Policy branch '%s' has %d heads; need 1 head") - % branch_desc.get_uid() % br.size()); - for (policy_branch::iterator i = br.begin(); - i != br.end(); ++i) - { - W(F("Heads are: '%s'") % i->first); - } - return boost::shared_ptr(); + W(F("Policy branch '%s' has %d heads and cannot be automatically merged") + % branch_desc.get_uid() % br.num_heads()); + return policy_ptr(); } - return br.begin()->second; } break; } ============================================================ --- policies/editable_policy.cc d9bdbd2e50c5cc7ca087ab9aa54c813ecb423b33 +++ policies/editable_policy.cc 723c66e1b8225aa2a70c3088c463e38b31713d70 @@ -17,6 +17,15 @@ namespace policies { : policy(p) { } + void editable_policy::clear() + { + keys.clear(); + branches.clear(); + tags.clear(); + delegations.clear(); + parent.reset(); + } + void editable_policy::set_parent(boost::weak_ptr const & parent) { this->parent = parent; ============================================================ --- policies/editable_policy.hh 6c22ef4e400ff2487af125981e6793d4b52f280f +++ policies/editable_policy.hh 9c71d01df39b3e6b8be7e0d74df7cd1e8173d744 @@ -20,6 +20,8 @@ namespace policies { editable_policy(); explicit editable_policy(policy const & p); + void clear(); + void set_parent(boost::weak_ptr const & parent); void set_key(key_name const & name, key_id const & ident); ============================================================ --- policies/policy.hh 167080f8a791cd65a87a08fea69980e14787cae2 +++ policies/policy.hh 7accc7c4f749b975d0dfc1ec1ab427f65d2a6a46 @@ -37,9 +37,8 @@ namespace policies { boost::weak_ptr parent; + public: policy(); - - public: virtual ~policy(); // keys ============================================================ --- policies/policy_branch.cc 740286a7466517294d7660f19e089957ef80d661 +++ policies/policy_branch.cc d359facea38e2d4e66e455e26ef1ae4e37c873b8 @@ -103,15 +103,11 @@ namespace policies { } namespace policies { - policy_ptr policy_from_revision(project_t const & project, - policy_ptr owner, - revision_id const & rev) + void policy_from_roster(project_t const & project, + roster_t const & the_roster, + editable_policy & pol) { - roster_t the_roster; - project.db.get_roster(rev, the_roster); - policies::editable_policy pol; - pol.set_parent(owner); - + pol.clear(); for (item_lister i(the_roster, file_path_internal("branches"), project.db); @@ -151,67 +147,152 @@ namespace policies { key_id id = decode_hexenc_as(s, origin::internal); pol.set_key(key_name(i->first, origin::internal), id); } - - return policy_ptr(new policies::policy(pol)); } + policy_ptr policy_from_revision(project_t const & project, + policy_ptr owner, + revision_id const & rev) + { + roster_t the_roster; + project.db.get_roster(rev, the_roster); + policies::editable_policy pol; + pol.set_parent(owner); + policy_from_roster(project, the_roster, pol); + return policy_ptr(new policy(pol)); + } + policy_branch::policy_branch(project_t const & project, policy_ptr parent_policy, branch const & b) : spec_owner(parent_policy), spec(b) { - reload(project); + loaded = reload(project); } branch const & policy_branch::get_spec() const { return spec; } - - policy_branch::iterator policy_branch::begin() const + size_t policy_branch::num_heads() const { - return policies.begin(); + return _num_heads; } - policy_branch::iterator policy_branch::end() const - { - return policies.end(); + + namespace { + void get_heads(project_t const & project, + branch const & spec, + policy_ptr const & spec_owner, + std::set & heads) + { + heads.clear(); + std::set keys; + std::set const & key_names = spec.get_signers(); + for (std::set::const_iterator i = key_names.begin(); + i != key_names.end(); ++i) + { + id ident; + if (try_decode_hexenc((*i)(), ident)) + { + keys.insert(key_id(ident)); + } + else + { + key_name name = typecast_vocab(*i); + keys.insert(spec_owner->get_key_id(name)); + } + } + project.get_branch_heads(spec.get_uid(), + keys, + heads, + false); + } + void get_rosters(project_t const & project, + std::set const & heads, + parent_map & rosters) + { + rosters.clear(); + for (std::set::const_iterator h = heads.begin(); + h != heads.end(); ++h) + { + cached_roster cr; + project.db.get_roster(*h, cr); + rosters.insert(make_pair(*h, cr)); + } + if (heads.empty()) + { + rosters.insert(make_pair(revision_id(), + make_pair(roster_t_cp(new roster_t()), + marking_map_cp(new marking_map())))); + } + } + enum parent_result { parent_clean, parent_semiclean, parent_fail }; + parent_result try_merge_parents(project_t const & project, + parent_map const & parents, + roster_t & roster) + { + if (parents.size() > 2) + return parent_fail; + if (parents.size() == 1) + { + roster = *parents.begin()->second.first; + return parent_clean; + } + + + parent_map::const_iterator left = parents.begin(); + parent_map::const_iterator right = left; ++right; + + revision_id const & left_rid = left->first; + revision_id const & right_rid = right->first; + roster_t const & left_roster = *left->second.first; + roster_t const & right_roster = *right->second.first; + marking_map const & left_mm = *left->second.second; + marking_map const & right_mm = *right->second.second; + + std::set left_ancestors, right_ancestors; + project.db.get_uncommon_ancestors(left_rid, right_rid, + left_ancestors, + right_ancestors); + roster_merge_result merge_result; + roster_merge(left_roster, left_mm, left_ancestors, + right_roster, right_mm, right_ancestors, + merge_result); + roster = merge_result.roster; + + if (merge_result.is_clean()) + return parent_clean; + else + return parent_fail; // eh, don't bother with 'semiclean' + } } - size_t policy_branch::size() const - { - return policies.size(); - } - void policy_branch::reload(project_t const & project) + bool policy_branch::reload(project_t const & project) { - policies.clear(); std::set heads; - std::set keys; - std::set const & key_names = spec.get_signers(); - for (std::set::const_iterator i = key_names.begin(); - i != key_names.end(); ++i) - { - id ident; - if (try_decode_hexenc((*i)(), ident)) - { - keys.insert(key_id(ident)); - } - else - { - key_name name = typecast_vocab(*i); - keys.insert(spec_owner->get_key_id(name)); - } - } - project.get_branch_heads(spec.get_uid(), - keys, - heads, - false); + get_heads(project, spec, spec_owner, heads); + _num_heads = heads.size(); + parent_map rosters; + get_rosters(project, heads, rosters); + roster_t roster; + parent_result res = try_merge_parents(project, rosters, roster); + if (res == parent_fail) + return false; - for (std::set::const_iterator i = heads.begin(); - i != heads.end(); ++i) - { - policies.insert(make_pair(*i, policy_from_revision(project, - spec_owner, - *i))); - } + policy_from_roster(project, roster, my_policy); + my_policy.set_parent(spec_owner); + return true; } + bool policy_branch::try_get_policy(policy & pol) const + { + if (!loaded) + return false; + pol = my_policy; + return true; + } + void policy_branch::get_policy(policy & pol, origin::type ty) const + { + E(try_get_policy(pol), ty, + F("cannot sanely combine %d heads of policy") + % _num_heads); + } namespace { class content_putter @@ -256,65 +337,27 @@ namespace policies { key_store & keys, policy const & p, utf8 const & changelog, - policy_branch::iterator parent_1, - policy_branch::iterator parent_2) + origin::type ty) { + E(try_commit(project, keys, p, changelog), ty, + F("cannot automatically merge %d heads of policy branch") + % _num_heads); + } + bool policy_branch::try_commit(project_t & project, + key_store & keys, + policy const & p, + utf8 const & changelog) + { + std::set heads; + get_heads(project, spec, spec_owner, heads); parent_map parents; - if (parent_1 != end()) - { - cached_roster cr; - project.db.get_roster(parent_1->first, cr); - parents.insert(make_pair(parent_1->first, cr)); - } - if (parent_2 != end()) - { - cached_roster cr; - project.db.get_roster(parent_2->first, cr); - parents.insert(make_pair(parent_2->first, cr)); - } - roster_t new_roster; - if (parents.empty()) - { - // prevent creation of extra heads - I(begin() == end()); + get_rosters(project, heads, parents); - parents.insert(make_pair(revision_id(), - make_pair(roster_t_cp(new roster_t()), - marking_map_cp(new marking_map())))); - } - else if (parents.size() == 1) - { - new_roster = *parents.begin()->second.first; - } - else - { - parent_map::const_iterator left = parents.begin(); - parent_map::const_iterator right = left; ++right; + roster_t new_roster; + parent_result res = try_merge_parents(project, parents, new_roster); + if (res != parent_clean) + return false; - - revision_id const & left_rid = left->first; - revision_id const & right_rid = right->first; - roster_t const & left_roster = *left->second.first; - roster_t const & right_roster = *right->second.first; - marking_map const & left_mm = *left->second.second; - marking_map const & right_mm = *right->second.second; - - std::set left_ancestors, right_ancestors; - project.db.get_uncommon_ancestors(left_rid, right_rid, - left_ancestors, - right_ancestors); - roster_merge_result merge_result; - roster_merge(left_roster, left_mm, left_ancestors, - right_roster, right_mm, right_ancestors, - merge_result); - // should really check this after applying our changes - // (or check that the only conflicts are contents conflicts - // on items that we'll be overwriting) - E(merge_result.is_clean(), origin::user, - F("Cannot automatically merge policy branch")); - new_roster = merge_result.roster; - } - temp_node_id_source source; if (!new_roster.has_root()) @@ -397,6 +440,7 @@ namespace policies { date_t::now(), author); guard.commit(); + return true; } } ============================================================ --- policies/policy_branch.hh 473e09b6b2700615b229a868eaa122cd48658947 +++ policies/policy_branch.hh 2cf62bc1c862c968fa2b660796fcd79ddee1bab3 @@ -13,10 +13,12 @@ #include +#include "origin_type.hh" #include "policies/branch.hh" #include "policies/delegation.hh" -#include "policies/policy.hh" +#include "policies/editable_policy.hh" + class key_store; class project_t; @@ -26,50 +28,35 @@ namespace policies { revision_id const & rev); class policy_branch { - public: - typedef std::set > policy_set; - private: policy_ptr spec_owner; branch spec; - policy_set policies; - void reload(project_t const & project); + size_t _num_heads; + editable_policy my_policy; + bool loaded; + bool reload(project_t const & project); public: - typedef policy_set::const_iterator iterator; - policy_branch(project_t const & project, policy_ptr parent_policy, branch const & b); - //policy_branch(delegation const & d); branch const & get_spec() const; - policy create_initial_revision() const; - - iterator begin() const; - iterator end() const; - size_t size() const; - - void commit(project_t & project, - key_store & keys, - policy const & p, - utf8 const & changelog, - iterator parent_1, - iterator parent_2); - inline void commit(project_t & project, - key_store & keys, - policy const & p, - utf8 const & changelog, - iterator parent) - { - commit(project, keys, p, changelog, parent, end()); - } - inline void commit(project_t & project, - key_store & keys, - policy const & p, - utf8 const & changelog) - { - commit(project, keys, p, changelog, end()); - } + size_t num_heads() const; + // return false if we can't get a coherent policy, due to + // having multiple heads and they can't be auto-merged + bool try_get_policy(policy & pol) const; + // wraper that will E() for you + void get_policy(policy & pol, origin::type ty) const; + // return false if the commit fails, due to + // having multiple heads that can't be auto-merged + bool try_commit(project_t & project, key_store & keys, + policy const & pol, + utf8 const & message); + // wrapper that will E() for you + void commit(project_t & project, key_store & keys, + policy const & pol, + utf8 const & message, + origin::type ty); }; } ============================================================ --- project.cc 8b1e22897df1bd5e81ce646873f95b5331b395d3 +++ project.cc b6041bfe58f791918414f7e694d52d8ce23c9b3f @@ -1075,14 +1075,15 @@ project_t::put_tag(key_store & keys, info.back().policy, info.back().delegation.get_branch_spec()); - I(br.begin() != br.end()); - policies::editable_policy ep(*br.begin()->second); + policies::editable_policy ep; + br.get_policy(ep, origin::user); ep.set_tag(name.substr(info.back().full_policy_name.size() + 1), id); br.commit(*this, keys, ep, utf8((F("Set tag %s") % name).str(), - origin::internal)); + origin::internal), + origin::user); } }