# # # patch "automate.cc" # from [8284966cdc6eefa94ec1892cd12c3b8328a0d1df] # to [b4f2e4f163bed5056310111f625a20b7c65642ff] # # patch "mtn_cvs/mtn_automate.cc" # from [4cc6bc36cea53fc4a17db351cf8c2518f6ea52e1] # to [c94c4d55812761034a1606209cf1f229f866c50e] # # patch "mtn_cvs/mtn_automate.hh" # from [463f2dc405ef99a6cff9afeb8068055f2ed43965] # to [92864feedb9d5edd08f00c92d5abdf1a46ad9106] # ============================================================ --- automate.cc 8284966cdc6eefa94ec1892cd12c3b8328a0d1df +++ automate.cc b4f2e4f163bed5056310111f625a20b7c65642ff @@ -1781,343 +1781,6 @@ AUTOMATE(db_get, N_("DOMAIN NAME"), opti output << value(); } -// needed by find_newest_sync: check whether a revision has up to date synch information -static const char *const sync_prefix="x-sync-attr-"; -typedef std::map, attr_value> sync_map_t; - -static bool begins_with(const std::string &s, const std::string &sub) -{ std::string::size_type len=sub.size(); - if (s.size()first.second(),prefix)) - return true; - } - } - - // look for a certificate - std::vector< revision > certs; - app.db.get_revision_certs(rid,cert_name(sync_prefix+domain),certs); - return !certs.empty(); -} - -// Name: find_newest_sync -// Arguments: -// sync-domain -// branch (optional) -// Added in: 3.2 -// Purpose: -// Get the newest revision which has a sync certificate -// (or changed sync attributes) -// Output format: -// revision ID -// Error conditions: -// a runtime exception is thrown if no synchronized revisions are found -// in this domain -AUTOMATE(find_newest_sync, N_("DOMAIN [BRANCH]"), options::opts::none) -{ /* if workspace exists use it to determine branch (and starting revision?) - traverse tree upwards to find a synced revision, - then traverse tree downwards to find newest revision - - this assumes a linear and connected synch graph (which is true for CVS, - but might not appropriate for different RCSs) - */ - - utf8 branch=app.opts.branch_name; - if (args.size() == 2) - branch=idx(args,1); - else if (args.size() != 1) - throw usage(name); - set heads; - app.get_project().get_branch_heads(branch, heads); - revision_t rev; - revision_id rid; - std::string domain = idx(args,0)(); - - while (!heads.empty()) - { - rid = *heads.begin(); - L(FL("find_newest_sync: testing node %s") % rid); - app.db.get_revision(rid, rev); - heads.erase(heads.begin()); - // is there a more efficient way than to create a revision_t object? - if (is_synchronized(app,rid,rev,domain)) - break; - for (edge_map::const_iterator e = rev.edges.begin(); - e != rev.edges.end(); ++e) - { - if (!null_id(edge_old_revision(e))) - heads.insert(edge_old_revision(e)); - } - N(!heads.empty(), F("no synchronized revision found in branch %s for domain %s") - % branch % domain); - } - - set children; -continue_outer: - L(FL("find_newest_sync: testing children of %s") % rid); - app.db.get_revision_children(rid, children); - for (set::const_iterator i=children.begin(); - i!=children.end(); ++i) - { - app.db.get_revision(*i, rev); - if (is_synchronized(app,*i,rev,domain)) - { - rid=*i; - goto continue_outer; - } - } - output << rid; -} - -namespace -{ - namespace syms - { - symbol const clear("clear"); - symbol const attr("attr"); - symbol const set("set"); - } -} - -static inline void -parse_path(basic_io::parser & parser, split_path & sp) -{ - string s; - parser.str(s); - file_path_internal(s).split(sp); -} - -static void parse_attributes(std::string const& in, sync_map_t& result) -{ - basic_io::input_source source(in,"parse_attributes"); - basic_io::tokenizer tokenizer(source); - basic_io::parser parser(tokenizer); - - std::string t1, t2; - split_path p1; - while (parser.symp(syms::clear)) - { - parser.sym(); - parse_path(parser, p1); - parser.esym(syms::attr); - parser.str(t1); - pair new_pair(p1, attr_key(t1)); - safe_erase(result, new_pair); - } - while (parser.symp(syms::set)) - { - parser.sym(); - parse_path(parser, p1); - parser.esym(syms::attr); - parser.str(t1); - pair new_pair(p1, attr_key(t1)); - parser.esym(syms::value); - parser.str(t2); - safe_insert(result, make_pair(new_pair, attr_value(t2))); - } -} - -static sync_map_t get_sync_info(app_state &app, revision_id const& rid, string const& domain, int &depth) -{ - /* sync information is initially coded in DOMAIN: prefixed attributes - if information needs to be changed after commit then it gets - (base_revision_id+xdiff).gz encoded in certificates - - SPECIAL CASE of no parent: certificate is (40*' '+plain_data).gz encoded - */ - sync_map_t result; - - L(FL("get_sync_info: checking revision certificates %s") % rid); - std::vector< revision > certs; - app.db.get_revision_certs(rid,cert_name(sync_prefix+domain),certs); - I(certs.size()<=1); // FIXME: what to do with multiple certs ... - if (certs.size()==1) - { cert_value tv; - decode_base64(idx(certs,0).inner().value, tv); - std::string decomp_cert_val=xform(tv()); - I(decomp_cert_val.size()>constants::idlen+1); - I(decomp_cert_val[constants::idlen]=='\n'); - if (decomp_cert_val[0]!=' ') - { revision_id old_rid=revision_id(decomp_cert_val.substr(0,constants::idlen)); - result=get_sync_info(app,old_rid,domain,depth); - ++depth; - } - else depth=0; - parse_attributes(decomp_cert_val.substr(constants::idlen+1),result); - return result; - } - - revision_t rev; - app.db.get_revision(rid, rev); -// split_path path; -// file_path_internal(string(".")+sync_prefix+domain).split(path); - if (rev.edges.size()==1) - { - L(FL("get_sync_info: checking revision attributes %s") % rid); -// revision_t rev; -// app.db.get_revision(rid, rev); - roster_t ros; - marking_map mm; - app.db.get_roster(rid, ros, mm); - node_map const & nodes = ros.all_nodes(); - std::string prefix=domain+":"; - for (node_map::const_iterator i = nodes.begin(); - i != nodes.end(); ++i) - { - node_t node = i->second; - split_path sp; - ros.get_name(i->first, sp); - for (full_attr_map_t::const_iterator j = node->attrs.begin(); - j != node->attrs.end(); ++j) - { - if (begins_with(j->first(),prefix)) - { - if (j->second.first) // value is not undefined - result[std::make_pair(sp,j->first)]=j->second.second; - // else W(F("undefined value of %s %s\n") % sp % j->first()); - } - } - } - depth=0; - } - N(!result.empty(), F("no sync cerficate found in revision %s for domain %s") - % rid % domain); - return result; -} - -// Name: get_sync_info -// Arguments: -// revision -// sync-domain -// Added in: 3.2 -// Purpose: -// Get the sync information for a given revision -// Output format: -// sync-data -// Error conditions: -// a runtime exception is thrown if the data is unavailable -AUTOMATE(get_sync_info, N_("REVISION DOMAIN"), options::opts::none) -{ - if (args.size() != 2) - throw usage(name); - revision_id rid(idx(args,0)()); - string domain=idx(args,1)(); - int dummy=0; - sync_map_t result = get_sync_info(app,rid,domain,dummy); - basic_io::printer printer; - for (sync_map_t::const_iterator i = result.begin(); i != result.end(); ++i) - { - basic_io::stanza st; - st.push_file_pair(syms::set, file_path(i->first.first)); - st.push_str_pair(syms::attr, i->first.second()); - st.push_str_pair(syms::value, i->second()); - printer.print_stanza(st); - } - output.write(printer.buf.data(), printer.buf.size()); -} - -// Name: put_sync_info -// Arguments: -// revision -// sync-domain -// data -// Added in: 3.2 -// Purpose: -// Set the sync information for a given revision -// Output format: -// none -// Error conditions: -// a runtime exception is thrown if anything goes wrong -AUTOMATE(put_sync_info, N_("REVISION DOMAIN DATA"), options::opts::none) -{ - if (args.size() != 3) - throw usage(name); - revision_id rid(idx(args,0)()); - string domain=idx(args,1)(); - revision_t rev; - app.db.get_revision(rid, rev); - - static const int max_indirection_nest=30; - - std::string new_data=idx(args,2)(); - cert c; - for (edge_map::const_iterator e = rev.edges.begin(); - e != rev.edges.end(); ++e) - { - if (null_id(edge_old_revision(e))) continue; - try - { - int depth=0; - sync_map_t oldinfo=get_sync_info(app,edge_old_revision(e),domain,depth); - if (depth>=max_indirection_nest) continue; // do not nest deeper - - sync_map_t newinfo; - parse_attributes(new_data,newinfo); - - basic_io::printer printer; - for (sync_map_t::const_iterator o=oldinfo.begin(),n=newinfo.begin(); - o!=oldinfo.end() && n!=newinfo.end();) - { if (n==newinfo.end() || o->firstfirst - /*|| (o->first==n->first && o->second!=n->second)*/) - { basic_io::stanza st; - st.push_file_pair(syms::clear, file_path(o->first.first)); - st.push_str_pair(syms::attr, o->first.second()); - printer.print_stanza(st); - if (o->first==n->first) ++n; - ++o; - } - else ++n; - } - for (sync_map_t::const_iterator o=oldinfo.begin(),n=newinfo.begin(); - o!=oldinfo.end() && n!=newinfo.end();) - { if (o==oldinfo.end() || o->first>n->first - || (o->first==n->first && o->second!=n->second)) - { basic_io::stanza st; - st.push_file_pair(syms::set, file_path(n->first.first)); - st.push_str_pair(syms::attr, n->first.second()); - st.push_str_pair(syms::value, n->second()); - printer.print_stanza(st); - if (o->first==n->first) ++o; - ++n; - } - else ++o; - } - // printer.buf - if (printer.buf.size()>=new_data.size()) continue; - - I(edge_old_revision(e).inner()().size()==constants::idlen); - cert_value cv=cert_value(xform(edge_old_revision(e).inner()()+"\n"+printer.buf)); - make_simple_cert(rid.inner(),cert_name(sync_prefix+domain), cv, app, c); - revision rc(c); - packet_db_writer dbw(app); - dbw.consume_revision_cert(rc); - L(FL("sync info encoded as delta from %s") % edge_old_revision(e)); - return; - } - catch (informative_failure &er) {} - catch (std::runtime_error &er) {} - } - cert_value cv=cert_value(xform(string(constants::idlen,' ')+"\n"+new_data)); - make_simple_cert(rid.inner(),cert_name(sync_prefix+domain), cv, app, c); - revision rc(c); - packet_db_writer dbw(app); - dbw.consume_revision_cert(rc); - L(FL("sync info attached to %s") % rid); -} - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- mtn_cvs/mtn_automate.cc 4cc6bc36cea53fc4a17db351cf8c2518f6ea52e1 +++ mtn_cvs/mtn_automate.cc c94c4d55812761034a1606209cf1f229f866c50e @@ -22,6 +22,7 @@ void mtn_automate::check_interface_revis "requirements %s") % present % minimum); } +#if 0 revision_id mtn_automate::find_newest_sync(std::string const& domain, std::string const& branch) { std::vector args; args.push_back(domain); @@ -29,6 +30,7 @@ revision_id mtn_automate::find_newest_sy std::string result=automate("find_newest_sync",args); return revision_id(result); } +#endif std::string mtn_automate::get_option(std::string const& name) { @@ -83,6 +85,7 @@ parse_path(basic_io::parser & parser, sp file_path_internal(s).split(sp); } +#if 0 mtn_automate::sync_map_t mtn_automate::get_sync_info(revision_id const& rid, std::string const& domain) { std::vector args; args.push_back(rid.inner()()); @@ -109,6 +112,7 @@ mtn_automate::sync_map_t mtn_automate::g } return result; } +#endif file_id mtn_automate::put_file(file_data const& d, file_id const& base) { std::vector args; @@ -518,6 +522,7 @@ dump(file_path const& fp, string & out) { out=fp.as_internal(); } +#if 0 void mtn_automate::put_sync_info(revision_id const& rid, std::string const& domain, sync_map_t const& data) { std::vector args; args.push_back(rid.inner()()); @@ -534,3 +539,299 @@ void mtn_automate::put_sync_info(revisio args.push_back(printer.buf); automate("put_sync_info",args); } +#endif + +// needed by find_newest_sync: check whether a revision has up to date synch information +static const char *const sync_prefix="x-sync-attr-"; +typedef std::map, attr_value> sync_map_t; + +static bool begins_with(const std::string &s, const std::string &sub) +{ std::string::size_type len=sub.size(); + if (s.size() cs=rev.edges.begin()->second; + for (std::map, attr_value>::const_iterator i=cs->attrs_set.begin(); + i!=cs->attrs_set.end();++i) + { if (begins_with(i->first.second(),prefix)) // TODO: omit some attribute changes (repository, keyword, directory) + return true; + } + } + + // look for a certificate + std::vector certs; + certs=get_revision_certs(rid,cert_name(sync_prefix+domain)); + return !certs.empty(); +} + +#if 0 +// Name: find_newest_sync +// Arguments: +// sync-domain +// branch (optional) +// Added in: 3.2 +// Purpose: +// Get the newest revision which has a sync certificate +// (or changed sync attributes) +// Output format: +// revision ID +// Error conditions: +// a runtime exception is thrown if no synchronized revisions are found +// in this domain +revision_id mtn_automate::find_newest_sync(std::string const& domain, std::string const& branch) +{ /* if workspace exists use it to determine branch (and starting revision?) + traverse tree upwards to find a synced revision, + then traverse tree downwards to find newest revision + + this assumes a linear and connected synch graph (which is true for CVS, + but might not appropriate for different RCSs) + */ + + set heads; + app.get_project().get_branch_heads(branch, heads); + revision_t rev; + revision_id rid; + std::string domain = idx(args,0)(); + + while (!heads.empty()) + { + rid = *heads.begin(); + L(FL("find_newest_sync: testing node %s") % rid); + app.db.get_revision(rid, rev); + heads.erase(heads.begin()); + // is there a more efficient way than to create a revision_t object? + if (is_synchronized(app,rid,rev,domain)) + break; + for (edge_map::const_iterator e = rev.edges.begin(); + e != rev.edges.end(); ++e) + { + if (!null_id(edge_old_revision(e))) + heads.insert(edge_old_revision(e)); + } + N(!heads.empty(), F("no synchronized revision found in branch %s for domain %s") + % branch % domain); + } + + set children; +continue_outer: + L(FL("find_newest_sync: testing children of %s") % rid); + app.db.get_revision_children(rid, children); + for (set::const_iterator i=children.begin(); + i!=children.end(); ++i) + { + app.db.get_revision(*i, rev); + if (is_synchronized(app,*i,rev,domain)) + { + rid=*i; + goto continue_outer; + } + } + output << rid; +} + +static inline void +parse_path(basic_io::parser & parser, split_path & sp) +{ + string s; + parser.str(s); + file_path_internal(s).split(sp); +} + +static void parse_attributes(std::string const& in, sync_map_t& result) +{ + basic_io::input_source source(in,"parse_attributes"); + basic_io::tokenizer tokenizer(source); + basic_io::parser parser(tokenizer); + + std::string t1, t2; + split_path p1; + while (parser.symp(syms::clear)) + { + parser.sym(); + parse_path(parser, p1); + parser.esym(syms::attr); + parser.str(t1); + pair new_pair(p1, attr_key(t1)); + safe_erase(result, new_pair); + } + while (parser.symp(syms::set)) + { + parser.sym(); + parse_path(parser, p1); + parser.esym(syms::attr); + parser.str(t1); + pair new_pair(p1, attr_key(t1)); + parser.esym(syms::value); + parser.str(t2); + safe_insert(result, make_pair(new_pair, attr_value(t2))); + } +} + +static sync_map_t get_sync_info(revision_id const& rid, string const& domain, int &depth) +{ + /* sync information is initially coded in DOMAIN: prefixed attributes + if information needs to be changed after commit then it gets + (base_revision_id+xdiff).gz encoded in certificates + + SPECIAL CASE of no parent: certificate is (40*' '+plain_data).gz encoded + */ + sync_map_t result; + + L(FL("get_sync_info: checking revision certificates %s") % rid); + std::vector< revision > certs; + app.db.get_revision_certs(rid,cert_name(sync_prefix+domain),certs); + I(certs.size()<=1); // FIXME: what to do with multiple certs ... + if (certs.size()==1) + { cert_value tv; + decode_base64(idx(certs,0).inner().value, tv); + std::string decomp_cert_val=xform(tv()); + I(decomp_cert_val.size()>constants::idlen+1); + I(decomp_cert_val[constants::idlen]=='\n'); + if (decomp_cert_val[0]!=' ') + { revision_id old_rid=revision_id(decomp_cert_val.substr(0,constants::idlen)); + result=get_sync_info(app,old_rid,domain,depth); + ++depth; + } + else depth=0; + parse_attributes(decomp_cert_val.substr(constants::idlen+1),result); + return result; + } + + revision_t rev; + app.db.get_revision(rid, rev); +// split_path path; +// file_path_internal(string(".")+sync_prefix+domain).split(path); + if (rev.edges.size()==1) + { + L(FL("get_sync_info: checking revision attributes %s") % rid); +// revision_t rev; +// app.db.get_revision(rid, rev); + roster_t ros; + marking_map mm; + app.db.get_roster(rid, ros, mm); + node_map const & nodes = ros.all_nodes(); + std::string prefix=domain+":"; + for (node_map::const_iterator i = nodes.begin(); + i != nodes.end(); ++i) + { + node_t node = i->second; + split_path sp; + ros.get_name(i->first, sp); + for (full_attr_map_t::const_iterator j = node->attrs.begin(); + j != node->attrs.end(); ++j) + { + if (begins_with(j->first(),prefix)) + { + if (j->second.first) // value is not undefined + result[std::make_pair(sp,j->first)]=j->second.second; + // else W(F("undefined value of %s %s\n") % sp % j->first()); + } + } + } + depth=0; + } + N(!result.empty(), F("no sync cerficate found in revision %s for domain %s") + % rid % domain); + return result; +} + +// Name: get_sync_info +// Arguments: +// revision +// sync-domain +// Purpose: +// Get the sync information for a given revision +mtn_automate::sync_map_t mtn_automate::get_sync_info(revision_id const& rid, std::string const& domain) +{ + int dummy=0; + return get_sync_info(rid,domain,dummy); +} + +// Name: put_sync_info +// Arguments: +// revision +// sync-domain +// data +// Purpose: +// Set the sync information for a given revision +void mtn_automate::put_sync_info(revision_id const& rid, std::string const& domain, sync_map_t const& new_data) +{ + revision_t rev=get_revision(rid); + + static const int max_indirection_nest=30; + + cert c; + for (edge_map::const_iterator e = rev.edges.begin(); + e != rev.edges.end(); ++e) + { + if (null_id(edge_old_revision(e))) continue; + try + { + int depth=0; + sync_map_t oldinfo=get_sync_info(app,edge_old_revision(e),domain,depth); + if (depth>=max_indirection_nest) continue; // do not nest deeper + + sync_map_t newinfo; + parse_attributes(new_data,newinfo); + + basic_io::printer printer; + for (sync_map_t::const_iterator o=oldinfo.begin(),n=newinfo.begin(); + o!=oldinfo.end() && n!=newinfo.end();) + { if (n==newinfo.end() || o->firstfirst + /*|| (o->first==n->first && o->second!=n->second)*/) + { basic_io::stanza st; + st.push_file_pair(syms::clear, file_path(o->first.first)); + st.push_str_pair(syms::attr, o->first.second()); + printer.print_stanza(st); + if (o->first==n->first) ++n; + ++o; + } + else ++n; + } + for (sync_map_t::const_iterator o=oldinfo.begin(),n=newinfo.begin(); + o!=oldinfo.end() && n!=newinfo.end();) + { if (o==oldinfo.end() || o->first>n->first + || (o->first==n->first && o->second!=n->second)) + { basic_io::stanza st; + st.push_file_pair(syms::set, file_path(n->first.first)); + st.push_str_pair(syms::attr, n->first.second()); + st.push_str_pair(syms::value, n->second()); + printer.print_stanza(st); + if (o->first==n->first) ++o; + ++n; + } + else ++o; + } + // printer.buf + if (printer.buf.size()>=new_data.size()) continue; + + I(edge_old_revision(e).inner()().size()==constants::idlen); + cert_value cv=cert_value(xform(edge_old_revision(e).inner()()+"\n"+printer.buf)); + make_simple_cert(rid.inner(),cert_name(sync_prefix+domain), cv, app, c); + revision rc(c); + packet_db_writer dbw(app); + dbw.consume_revision_cert(rc); + L(FL("sync info encoded as delta from %s") % edge_old_revision(e)); + return; + } + catch (informative_failure &er) {} + catch (std::runtime_error &er) {} + } + cert_value cv=cert_value(xform(string(constants::idlen,' ')+"\n"+new_data)); + make_simple_cert(rid.inner(),cert_name(sync_prefix+domain), cv, app, c); + revision rc(c); + packet_db_writer dbw(app); + dbw.consume_revision_cert(rc); + L(FL("sync info attached to %s") % rid); +} + +#endif ============================================================ --- mtn_cvs/mtn_automate.hh 463f2dc405ef99a6cff9afeb8068055f2ed43965 +++ mtn_cvs/mtn_automate.hh 92864feedb9d5edd08f00c92d5abdf1a46ad9106 @@ -62,6 +62,7 @@ struct mtn_automate : mtn_pipe revision_id put_revision(revision_id const& parent, cset const& changes); void cert_revision(revision_id const& rid, std::string const& name, std::string const& value); std::vector get_revision_certs(revision_id const& rid); + std::vector get_revision_certs(revision_id const& rid, cert_name const& name); file_data get_file(file_id const& fid); std::vector get_revision_children(revision_id const& rid); std::vector get_revision_parents(revision_id const& rid); @@ -69,6 +70,9 @@ struct mtn_automate : mtn_pipe std::vector heads(std::string const& branch); std::string get_option(std::string const& name); + +private: + bool is_synchronized(revision_id const& rid, revision_t const& rev, std::string const& domain); }; #endif