# # # add_file "branch.cc" # content [feebd0dcee184b06da556c39804f799df62004d1] # # add_file "branch.hh" # content [f913da1a88fee4f7943411cb8d03a11655ca5d2e] # # add_file "outdated_indicator.cc" # content [5aec489052b464a928cadf8915478177f7ee8196] # # add_file "outdated_indicator.hh" # content [1e93604846e99249f6f6e31544b39d6fb576a3ab] # # patch "ChangeLog" # from [17b3fafd9646930a3de6c6613c11398ac0dcdd31] # to [e2c18e954bbf2da27e8e25e744955774a1fddc9f] # # patch "Makefile.am" # from [54ead20fb82676cc06060406a7ff85fcc63d92e8] # to [a1bb9b6a8e0323dab754103d76ff302fa08cb6e6] # # patch "app_state.cc" # from [3f05dfd982acfdbfc89686994668285190de5933] # to [c51dd3ba75b38b6f0c624470f5b88f976c3b1deb] # # patch "app_state.hh" # from [bd83327f6bfc72eb55500a114abf147b687f5c79] # to [542ae07fd61ea2002549a185189c0db1235613bc] # # patch "automate.cc" # from [d84bcc2a3343891d6702ac80c8c00672aea6037b] # to [7d5b68647c134c094323e2ac3793d887211547a0] # # patch "cert.cc" # from [5936182817276e51a90b8be71753cd5ae3eb6d9e] # to [7b6c1ee29936bcdcb470a76fa415a750c77bc51a] # # patch "cert.hh" # from [797765e33d5bec49457a3ff0e09374ee6317949c] # to [9802985dc9185a37739575d5a4c84d41f4acd286] # # patch "cmd_merging.cc" # from [f578cdba90e5b74d001e48bab8db95870c04fce9] # to [6cf691238da422820cb4e59a05e0174a54548c93] # # patch "cmd_ws_commit.cc" # from [7c6997b990823c878080f41b0428b2543fa0c896] # to [b7434c5291a0c867f38fdbff6a1a7bae50a90cbb] # # patch "database.cc" # from [2d10672483bebf2fffbd7c1ac0edf6ddbe322f98] # to [63484ce0117ff482396a9371445363bd8071dab4] # # patch "database.hh" # from [bbf8e39a0adaa28a4694549d26602b3712f6109c] # to [90b06ee358aefaec065b5e7a7ac645f112dade68] # ============================================================ --- branch.cc feebd0dcee184b06da556c39804f799df62004d1 +++ branch.cc feebd0dcee184b06da556c39804f799df62004d1 @@ -0,0 +1,19 @@ +// 2007 Timothy Brownawell +// GNU GPL V2 or later + +#include "branch.hh" +#include "cert.hh" + +branch::branch(app_state & app, utf8 const & name) + : app(app), name(name) +{} + +void +branch::heads(std::set & h) +{ + if (stamp.outdated()) + { + stamp = get_branch_heads(name(), app, _heads); + } + h = _heads; +} ============================================================ --- branch.hh f913da1a88fee4f7943411cb8d03a11655ca5d2e +++ branch.hh f913da1a88fee4f7943411cb8d03a11655ca5d2e @@ -0,0 +1,26 @@ +// 2007 Timothy Brownawell +// GNU GPL V2 or later + +#ifndef __BRANCH_HH__ +#define __BRANCH_HH__ + +#include + +#include "outdated_indicator.hh" +#include "vocab.hh" + +class app_state; + +class branch +{ + app_state & app; + utf8 name; + outdated_indicator stamp; + std::set _heads; +public: + branch(app_state & app, utf8 const & name); + + void heads(std::set & h); +}; + +#endif ============================================================ --- outdated_indicator.cc 5aec489052b464a928cadf8915478177f7ee8196 +++ outdated_indicator.cc 5aec489052b464a928cadf8915478177f7ee8196 @@ -0,0 +1,108 @@ +// 2007 Timothy Brownawell +// GNU GPL V2 or later + +#include "outdated_indicator.hh" +#include "sanity.hh" + +class outdated_indicator_factory_impl +{ + unsigned int changed; + unsigned int dispensed; +public: + outdated_indicator_factory_impl(); + void note_change(); + unsigned int last_change() const; + unsigned int dispense(); +}; + +outdated_indicator_factory_impl::outdated_indicator_factory_impl() + : changed(0), dispensed(0) +{} + +unsigned int +outdated_indicator_factory_impl::last_change() const +{ + return changed; +} + +unsigned int +outdated_indicator_factory_impl::dispense() +{ + I(changed == dispensed || changed == dispensed + 1); + dispensed = changed; + return dispensed; +} + +void +outdated_indicator_factory_impl::note_change() +{ + I(changed == dispensed || changed == dispensed + 1); + if (changed == dispensed) + ++changed; +} + + +outdated_indicator::outdated_indicator() + : parent(), when(0) +{} + +outdated_indicator::outdated_indicator(boost::shared_ptr p) + : parent(p), when(p->dispense()) +{} + +bool +outdated_indicator::outdated() +{ + if (parent) + { + I(when <= parent->last_change()); + return when < parent->last_change(); + } + else + return true; +} + + +outdated_indicator_factory::outdated_indicator_factory() + : impl(new outdated_indicator_factory_impl) +{} + +outdated_indicator_factory::~outdated_indicator_factory() +{ + impl->note_change(); +} + +outdated_indicator +outdated_indicator_factory::get_notifier() +{ + return outdated_indicator(impl); +} + +void +outdated_indicator_factory::note_change() +{ + impl->note_change(); +} + +#ifdef BUILD_UNIT_TESTS +#include "unit_tests.hh" + +UNIT_TEST(outdated_indicator, ) +{ + outdated_indicator indicator; + { + outdated_indicator_factory factory; + BOOST_CHECK(indicator.outdated()); + indicator = factory.get_notifier(); + BOOST_CHECK(!indicator.outdated()); + factory.note_change(); + BOOST_CHECK(indicator.outdated()); + factory.note_change(); + factory.note_change(); + indicator = factory.get_notifier(); + BOOST_CHECK(!indicator.outdated()); + } + BOOST_CHECK(indicator.outdated()); +} + +#endif ============================================================ --- outdated_indicator.hh 1e93604846e99249f6f6e31544b39d6fb576a3ab +++ outdated_indicator.hh 1e93604846e99249f6f6e31544b39d6fb576a3ab @@ -0,0 +1,44 @@ +#ifndef __OUTDATED_NOTIFIER_HH__ +#define __OUTDATED_NOTIFIER_HH__ + +// 2007 Timothy Brownawell +// GNU GPL V2 or later + +// Allow clients to find out when something changes. +// The 'something' has an outdated_indicator_factory, +// and calls note_change() when changes are made. +// The client is provided with an outdated_indicator made +// from that factory, which will become outdated after +// further changes are made to the something. + +// The default indicator is always outdated. + +// When a factory is destroyed, all indicators made from +// that factory become outdated. + +#include + +class outdated_indicator_factory_impl; + +class outdated_indicator +{ + boost::shared_ptr parent; + unsigned int when; +public: + outdated_indicator(); + explicit outdated_indicator(boost::shared_ptr p); + bool outdated(); +}; + + +class outdated_indicator_factory +{ + boost::shared_ptr impl; +public: + outdated_indicator_factory(); + ~outdated_indicator_factory(); + outdated_indicator get_notifier(); + void note_change(); +}; + +#endif ============================================================ --- ChangeLog 17b3fafd9646930a3de6c6613c11398ac0dcdd31 +++ ChangeLog e2c18e954bbf2da27e8e25e744955774a1fddc9f @@ -1,3 +1,16 @@ +2007-01-09 Timothy Brownawell + + * outdated_indicator.{cc,hh}: Allows code that caches replys to + find out when those replys may be outdated. Used by... + * branch.{cc,hh}: Give us a concept of a 'branch'. Currently, this + only lets you find branch heads. Branch heads are cached and only + recalculated if certs have been added to / removed from the db. + * database.{cc,hh}: Enable the use of outdated_indicator s for + operations that return sets of certs. + * many: Use the new way to calculate heads. Right now, the main + effect is that 'automate heads' of the same branch is very fast + on subsequent calls from 'automate stdio'. + 2007-01-06 Thomas Keller * cmd_automate.cc: automate stdio now opens the database ============================================================ --- Makefile.am 54ead20fb82676cc06060406a7ff85fcc63d92e8 +++ Makefile.am a1bb9b6a8e0323dab754103d76ff302fa08cb6e6 @@ -33,6 +33,8 @@ MOST_SOURCES = \ update.cc update.hh \ work.cc work_migration.cc work.hh \ cert.cc cert.hh \ + branch.cc branch.hh \ + outdated_indicator.cc outdated_indicator.hh \ database.cc database.hh \ key_store.cc key_store.hh \ localized_file_io.cc localized_file_io.hh \ ============================================================ --- app_state.cc 3f05dfd982acfdbfc89686994668285190de5933 +++ app_state.cc c51dd3ba75b38b6f0c624470f5b88f976c3b1deb @@ -193,6 +193,14 @@ app_state::make_branch_sticky() } } +branch & +app_state::get_branch(utf8 const & name) +{ + std::pair::iterator, bool> res; + res = branch_map.insert(std::make_pair(name, branch(*this, name))); + return res.first->second; +} + void app_state::set_root(system_path const & path) { ============================================================ --- app_state.hh bd83327f6bfc72eb55500a114abf147b687f5c79 +++ app_state.hh 542ae07fd61ea2002549a185189c0db1235613bc @@ -19,6 +19,7 @@ class lua_hooks; #include #include +#include "branch.hh" #include "database.hh" #include "key_store.hh" #include "lua_hooks.hh" @@ -80,6 +81,11 @@ public: void make_branch_sticky(); +private: + std::map branch_map; +public: + branch & get_branch(utf8 const & name); + void set_database(system_path const & filename); void set_key_dir(system_path const & filename); void set_root(system_path const & root); ============================================================ --- automate.cc d84bcc2a3343891d6702ac80c8c00672aea6037b +++ automate.cc 7d5b68647c134c094323e2ac3793d887211547a0 @@ -71,7 +71,7 @@ AUTOMATE(heads, N_("[BRANCH]"), options: app.opts.branch_name = idx(args, 0); } set heads; - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name()).heads(heads); for (set::const_iterator i = heads.begin(); i != heads.end(); ++i) output << (*i).inner()() << "\n"; } ============================================================ --- cert.cc 5936182817276e51a90b8be71753cd5ae3eb6d9e +++ cert.cc 7b6c1ee29936bcdcb470a76fa415a750c77bc51a @@ -565,7 +565,7 @@ namespace }; } -void +outdated_indicator get_branch_heads(cert_value const & branchname, app_state & app, set & heads) @@ -574,13 +574,15 @@ get_branch_heads(cert_value const & bran base64 branch_encoded; encode_base64(branchname, branch_encoded); - app.db.get_revisions_with_cert(cert_name(branch_cert_name), - branch_encoded, - heads); + outdated_indicator stamp; + stamp = app.db.get_revisions_with_cert(cert_name(branch_cert_name), + branch_encoded, + heads); not_in_branch p(app, branch_encoded); erase_ancestors_and_failures(heads, p, app); L(FL("found heads of branch %s (%s heads)") % branchname % heads.size()); + return stamp; } ============================================================ --- cert.hh 797765e33d5bec49457a3ff0e09374ee6317949c +++ cert.hh 9802985dc9185a37739575d5a4c84d41f4acd286 @@ -17,6 +17,7 @@ #include #include +#include "outdated_indicator.hh" #include "vocab.hh" // Certs associate an opaque name/value pair with a revision ID, and @@ -89,7 +90,7 @@ cert_revision_in_branch(revision_id cons app_state & app, packet_consumer & pc); -void +outdated_indicator get_branch_heads(cert_value const & branchname, app_state & app, std::set & heads); ============================================================ --- cmd_merging.cc f578cdba90e5b74d001e48bab8db95870c04fce9 +++ cmd_merging.cc 6cf691238da422820cb4e59a05e0174a54548c93 @@ -330,7 +330,7 @@ CMD(merge, N_("tree"), "", N_("merge unm F("please specify a branch, with --branch=BRANCH")); set heads; - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name()).heads(heads); N(heads.size() != 0, F("branch '%s' is empty") % app.opts.branch_name); if (heads.size() == 1) @@ -398,7 +398,7 @@ CMD(merge, N_("tree"), "", N_("merge unm ancestors.clear(); heads_for_ancestor.clear(); - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name()).heads(heads); pass++; } @@ -463,8 +463,8 @@ CMD(merge_into_dir, N_("tree"), N_("SOUR if (args.size() != 3) throw usage(name); - get_branch_heads(idx(args, 0)(), app, src_heads); - get_branch_heads(idx(args, 1)(), app, dst_heads); + app.get_branch(idx(args, 0)()).heads(src_heads); + app.get_branch(idx(args, 1)()).heads(dst_heads); N(src_heads.size() != 0, F("branch '%s' is empty") % idx(args, 0)()); N(src_heads.size() == 1, F("branch '%s' is not merged") % idx(args, 0)()); @@ -828,7 +828,7 @@ CMD(heads, N_("tree"), "", N_("show unme N(app.opts.branch_name() != "", F("please specify a branch, with --branch=BRANCH")); - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name()).heads(heads); if (heads.size() == 0) P(F("branch '%s' is empty") % app.opts.branch_name); ============================================================ --- cmd_ws_commit.cc 7c6997b990823c878080f41b0428b2543fa0c896 +++ cmd_ws_commit.cc b7434c5291a0c867f38fdbff6a1a7bae50a90cbb @@ -502,7 +502,7 @@ CMD(checkout, N_("tree"), N_("[DIRECTORY F("use --revision or --branch to specify what to checkout")); set heads; - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name).heads(heads); N(heads.size() > 0, F("branch '%s' is empty") % app.opts.branch_name); if (heads.size() > 1) @@ -758,7 +758,7 @@ CMD(commit, N_("workspace"), N_("[PATH]. I(restricted_rev.edges.size() == 1); set heads; - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name).heads(heads); unsigned int old_head_size = heads.size(); if (app.opts.branch_name() != "") @@ -931,7 +931,7 @@ CMD(commit, N_("workspace"), N_("[PATH]. app.work.blank_user_log(); - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name).heads(heads); if (heads.size() > old_head_size && old_head_size > 0) { P(F("note: this revision creates divergence\n" "note: you may (or may not) wish to run '%s merge'") @@ -1036,7 +1036,7 @@ CMD_NO_WORKSPACE(import, N_("tree"), N_( F("use --revision or --branch to specify what to checkout")); set heads; - get_branch_heads(app.opts.branch_name(), app, heads); + app.get_branch(app.opts.branch_name).heads(heads); if (heads.size() > 1) { P(F("branch %s has multiple heads:") % app.opts.branch_name); ============================================================ --- database.cc 2d10672483bebf2fffbd7c1ac0edf6ddbe322f98 +++ database.cc 63484ce0117ff482396a9371445363bd8071dab4 @@ -1911,6 +1911,7 @@ database::delete_existing_rev_and_certs( // Kill the certs, ancestry, and revision. execute(query("DELETE from revision_certs WHERE id = ?") % text(rid.inner()())); + cert_stamper.note_change(); execute(query("DELETE from revision_ancestry WHERE child = ?") % text(rid.inner()())); @@ -1931,6 +1932,7 @@ database::delete_branch_named(cert_value L(FL("Deleting all references to branch %s") % branch); execute(query("DELETE FROM revision_certs WHERE name='branch' AND value =?") % blob(branch())); + cert_stamper.note_change(); execute(query("DELETE FROM branch_epochs WHERE branch=?") % blob(branch())); } @@ -1942,6 +1944,7 @@ database::delete_tag_named(cert_value co L(FL("Deleting all references to tag %s") % tag); execute(query("DELETE FROM revision_certs WHERE name='tag' AND value =?") % blob(tag())); + cert_stamper.note_change(); } // crypto key management @@ -2241,10 +2244,12 @@ database::put_revision_cert(revision const & cert) { put_cert(cert.inner(), "revision_certs"); + cert_stamper.note_change(); } -void database::get_revision_cert_nobranch_index(vector< pair, - pair > > & idx) +outdated_indicator +database::get_revision_cert_nobranch_index(vector< pair, + pair > > & idx) { results res; fetch(res, 3, any_rows, @@ -2259,18 +2264,20 @@ void database::get_revision_cert_nobranc make_pair(revision_id((*i)[1]), rsa_keypair_id((*i)[2])))); } + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(vector< revision > & ts) { vector certs; get_certs(certs, "revision_certs"); ts.clear(); copy(certs.begin(), certs.end(), back_inserter(ts)); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(cert_name const & name, vector< revision > & ts) { @@ -2278,9 +2285,10 @@ database::get_revision_certs(cert_name c get_certs(name, certs, "revision_certs"); ts.clear(); copy(certs.begin(), certs.end(), back_inserter(ts)); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(revision_id const & id, cert_name const & name, vector< revision > & ts) @@ -2289,9 +2297,10 @@ database::get_revision_certs(revision_id get_certs(id.inner(), name, certs, "revision_certs"); ts.clear(); copy(certs.begin(), certs.end(), back_inserter(ts)); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(revision_id const & id, cert_name const & name, base64 const & val, @@ -2301,9 +2310,10 @@ database::get_revision_certs(revision_id get_certs(id.inner(), name, val, certs, "revision_certs"); ts.clear(); copy(certs.begin(), certs.end(), back_inserter(ts)); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revisions_with_cert(cert_name const & name, base64 const & val, set & revisions) @@ -2316,9 +2326,10 @@ database::get_revisions_with_cert(cert_n fetch(res, one_col, any_rows, q % text(name()) % blob(binvalue())); for (results::const_iterator i = res.begin(); i != res.end(); ++i) revisions.insert(revision_id((*i)[0])); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(cert_name const & name, base64 const & val, vector< revision > & ts) @@ -2327,9 +2338,10 @@ database::get_revision_certs(cert_name c get_certs(name, val, certs, "revision_certs"); ts.clear(); copy(certs.begin(), certs.end(), back_inserter(ts)); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(revision_id const & id, vector< revision > & ts) { @@ -2337,9 +2349,10 @@ database::get_revision_certs(revision_id get_certs(id.inner(), certs, "revision_certs"); ts.clear(); copy(certs.begin(), certs.end(), back_inserter(ts)); + return cert_stamper.get_notifier(); } -void +outdated_indicator database::get_revision_certs(revision_id const & ident, vector< hexenc > & ts) { @@ -2353,6 +2366,7 @@ database::get_revision_certs(revision_id ts.clear(); for (size_t i = 0; i < res.size(); ++i) ts.push_back(hexenc(res[i][0])); + return cert_stamper.get_notifier(); } void ============================================================ --- database.hh bbf8e39a0adaa28a4694549d26602b3712f6109c +++ database.hh 90b06ee358aefaec065b5e7a7ac645f112dade68 @@ -28,6 +28,7 @@ int sqlite3_finalize(sqlite3_stmt *); #include "cleanup.hh" #include "roster.hh" #include "selectors.hh" +#include "outdated_indicator.hh" #include "vocab.hh" #include "rev_height.hh" @@ -407,42 +408,44 @@ private: std::vector & certs, std::string const & table); + outdated_indicator_factory cert_stamper; public: + bool revision_cert_exists(revision const & cert); bool revision_cert_exists(hexenc const & hash); void put_revision_cert(revision const & cert); // this variant has to be rather coarse and fast, for netsync's use - void get_revision_cert_nobranch_index(std::vector< std::pair, - std::pair > > & idx); + outdated_indicator get_revision_cert_nobranch_index(std::vector< std::pair, + std::pair > > & idx); - void get_revision_certs(std::vector< revision > & certs); + outdated_indicator get_revision_certs(std::vector< revision > & certs); - void get_revision_certs(cert_name const & name, + outdated_indicator get_revision_certs(cert_name const & name, std::vector< revision > & certs); - void get_revision_certs(revision_id const & ident, + outdated_indicator get_revision_certs(revision_id const & ident, cert_name const & name, std::vector< revision > & certs); - void get_revision_certs(cert_name const & name, + outdated_indicator get_revision_certs(cert_name const & name, base64 const & val, std::vector< revision > & certs); - void get_revision_certs(revision_id const & ident, + outdated_indicator get_revision_certs(revision_id const & ident, cert_name const & name, base64 const & value, std::vector< revision > & certs); - void get_revisions_with_cert(cert_name const & name, + outdated_indicator get_revisions_with_cert(cert_name const & name, base64 const & value, std::set & revisions); - void get_revision_certs(revision_id const & ident, + outdated_indicator get_revision_certs(revision_id const & ident, std::vector< revision > & certs); - void get_revision_certs(revision_id const & ident, + outdated_indicator get_revision_certs(revision_id const & ident, std::vector< hexenc > & hashes); void get_revision_cert(hexenc const & hash,