# # # add_file "mtn_cvs/.cproject" # content [a1f20b436aa965b114970ee9ea2529b96ec57f61] # # add_file "mtn_cvs/.project" # content [3e72df7ec76afaddd9a8da0f63fce0992e25ff4e] # # add_file "mtn_cvs/cvs_sync_pull.cc" # content [b6c307907836f4ba9143a60df584e7bb2783c81c] # # add_file "mtn_cvs/cvs_sync_push.cc" # content [4b19c7dd823bf02e72896eba7cd4a41de43718bf] # # add_file "mtn_cvs/cvs_sync_takeover.cc" # content [89ecce5569183386f4e6e686babe821b6ebfca3d] # # patch "mtn_cvs/Makefile.am" # from [09bb01b17cbf25fbe3d7744d461b035aef617307] # to [ef9c574400c98dbb33e4509bdba2fe165d83af1f] # # patch "mtn_cvs/cvs_sync.cc" # from [e3ca21e430e1de41b51a217b1f9e03eb92eac5f0] # to [b55447689889185eb3ecb009847b2bae65c887b7] # # patch "mtn_cvs/cvs_sync.hh" # from [1fca0a84ef4b6803903751bfbdfc56ea5f281650] # to [691da87c1325aa135a3763b60b3e16a0c3a7e99a] # ============================================================ --- mtn_cvs/.cproject a1f20b436aa965b114970ee9ea2529b96ec57f61 +++ mtn_cvs/.cproject a1f20b436aa965b114970ee9ea2529b96ec57f61 @@ -0,0 +1,930 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ============================================================ --- mtn_cvs/.project 3e72df7ec76afaddd9a8da0f63fce0992e25ff4e +++ mtn_cvs/.project 3e72df7ec76afaddd9a8da0f63fce0992e25ff4e @@ -0,0 +1,82 @@ + + + mtn_cvs + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.buildCommand + make + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/mtn_cvs} + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.buildArguments + + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + + + + + + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.cnature + + ============================================================ --- mtn_cvs/cvs_sync_pull.cc b6c307907836f4ba9143a60df584e7bb2783c81c +++ mtn_cvs/cvs_sync_pull.cc b6c307907836f4ba9143a60df584e7bb2783c81c @@ -0,0 +1,740 @@ +// copyright (C) 2005-2006 Christof Petig +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#include "cvs_sync.hh" +#include "mtncvs_state.hh" +#include "keys.hh" +#include "transforms.hh" +#include +#include +#include "botan/md5.h" +#include +#include +#include +#include +#include "stringtok.hh" +#include "piece_table.hh" +#include "safe_map.hh" +#include + +#ifdef WIN32 +#define sleep(x) _sleep(x) +#endif + +using namespace std; +using namespace cvs_sync; + +void cvs_repository::store_contents(file_data const &dat, file_id &sha1sum) +{ + if (file_id_ticker.get()) ++(*file_id_ticker); + sha1sum=app.put_file(dat); +} + +static void apply_delta(piece::piece_table &contents, const std::string &patch) +{ piece::piece_table after; + piece::apply_diff(contents,after,patch); + std::swap(contents,after); +} + +void cvs_repository::store_delta(file_data const& new_contents, + file_data const& old_contents, + file_id const&from, file_id &to) +{ if (old_contents.inner()().empty()) + { store_contents(new_contents, to); + return; + } + if (file_id_ticker.get()) ++(*file_id_ticker); + to=app.put_file(new_contents,from); +} + +void cvs_repository::store_update(std::set::const_iterator s, + std::set::iterator s2,const cvs_client::update &u, + std::string &contents) +{ + if (u.removed) + { const_cast(s2->dead)=true; + } + else if (!u.checksum.empty()) + { // const_cast(s2->rcs_patch)=u.patch; + const_cast(s2->md5sum)=u.checksum; + const_cast(s2->patchsize)=u.patch.size(); + const_cast(s2->keyword_substitution)=u.keyword_substitution; + // I(s2->since_when==u.mod_time); + if (u.mod_time!=s2->since_when && u.mod_time!=-1) + { W(F("update time %s and log time %s disagree\n") % time_t2human(u.mod_time) % time_t2human(s2->since_when)); + } + std::string old_contents=contents; + { piece::piece_table file_contents; + piece::index_deltatext(contents,file_contents); + apply_delta(file_contents, u.patch); + piece::build_string(file_contents, contents); + piece::reset(); + } + // check md5 + Botan::MD5 hash; + std::string md5sum=xform(u.checksum); + I(md5sum.size()==hash.OUTPUT_LENGTH); + Botan::SecureVector hashval=hash.process(contents); + I(hashval.size()==hash.OUTPUT_LENGTH); + unsigned hashidx=hash.OUTPUT_LENGTH; + for (;hashidx && hashval[hashidx-1]==Botan::byte(md5sum[hashidx-1]);--hashidx) ; + if (!hashidx) + { store_delta(file_data(contents), file_data(old_contents), s->sha1sum, + const_cast(s2->sha1sum)); + } + else + { E(false, F("MD5 sum %s<>%s") % u.checksum + % xform(std::string(hashval.begin(),hashval.end()))); + } + } + else + { if (!s->sha1sum.inner()().empty()) + // we default to patch if it's at all possible + store_delta(file_data(u.contents), file_data(contents), s->sha1sum, const_cast(s2->sha1sum)); + else + store_contents(file_data(u.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=u.contents.size(); + contents=u.contents; + const_cast(s2->keyword_substitution)=u.keyword_substitution; + } +} + +// s2 gets changed +void cvs_repository::update(std::set::const_iterator s, + std::set::iterator s2,const std::string &file, + std::string &contents) +{ + cvs_revision_nr srev(s->cvs_version); + MM(file); + MM(s->cvs_version); + MM(s2->cvs_version); + if (!srev.is_parent_of(s2->cvs_version)) + std::cerr << "Inconsistency "<< file << ": " << s->cvs_version + << "->" << s2->cvs_version << "\n" << debug() << '\n'; + I(srev.is_parent_of(s2->cvs_version)); + if (s->dead) + { + // this might fail (?) because we issued an Entry somewhere above + // but ... we can specify the correct directory! + cvs_client::update c=Update(file,s2->cvs_version); + I(!c.removed); // dead->dead is no change, so shouldn't get a number + I(!s2->dead); + // I(s2->since_when==c.mod_time); + if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) + { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); + } + store_contents(file_data(c.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=c.contents.size(); + contents=c.contents; + const_cast(s2->keyword_substitution)=c.keyword_substitution; + } + else if (s2->dead) // short circuit if we already know it's dead + { L(FL("file %s: revision %s already known to be dead\n") % file % s2->cvs_version); + } + else + { cvs_client::update u=Update(file,s->cvs_version,s2->cvs_version,s->keyword_substitution); + try + { store_update(s,s2,u,contents); + } catch (informative_failure &e) + { W(F("Update: patching failed with %s\n") % e.what()); + cvs_client::update c=Update(file,s2->cvs_version); + if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) + { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); + } + const_cast(s2->md5sum)=""; + const_cast(s2->patchsize)=0; + store_contents(file_data(c.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=c.contents.size(); + contents=c.contents; + const_cast(s2->keyword_substitution)=c.keyword_substitution; + } catch (std::exception &e) + { W(F("Update: patching failed with %s\n") % e.what()); + cvs_client::update c=Update(file,s2->cvs_version); + if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) + { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); + } + const_cast(s2->md5sum)=""; + const_cast(s2->patchsize)=0; + store_contents(file_data(c.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=c.contents.size(); + contents=c.contents; + const_cast(s2->keyword_substitution)=c.keyword_substitution; + } + } +} + +void cvs_repository::store_checkout(std::set::iterator s2, + const cvs_client::checkout &c, std::string &file_contents) +{ const_cast(s2->dead)=c.dead; + if (!c.dead) + { // I(c.mod_time==s2->since_when); + if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) + { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); + } + store_contents(file_data(c.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=c.contents.size(); + file_contents=c.contents; + const_cast(s2->keyword_substitution)=c.keyword_substitution; + const_cast(s2->mode)=c.mode; + } +} + +void cvs_repository::store_checkout(std::set::iterator s2, + const cvs_client::update &c, std::string &file_contents) +{ const_cast(s2->dead)=c.removed; + if (!c.removed) + { // I(c.mod_time==s2->since_when); + if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) + { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); + } + store_contents(file_data(c.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=c.contents.size(); + file_contents=c.contents; + const_cast(s2->keyword_substitution)=c.keyword_substitution; + const_cast(s2->mode)=c.mode; + } +} + +void cvs_repository::attach_sync_state(cvs_edge & e,mtn_automate::manifest_map const& oldmanifest, + mtn_automate::cset &cs) +{ mtn_automate::sync_map_t state=create_sync_state(e); + bool any_change=false; + // added and changed attributes + for (mtn_automate::sync_map_t::const_iterator i=state.begin(); + i!=state.end(); ++i) + { + mtn_automate::manifest_map::const_iterator f + = oldmanifest.find(file_path(i->first.first)); + if (f==oldmanifest.end()) + { // only add attributes on existing nodes + if (cs.dirs_added.find(i->first.first)!=cs.dirs_added.end() + || cs.files_added.find(i->first.first)!=cs.files_added.end()) + { cs.attrs_set[i->first]=i->second; + any_change=true; + } + } + else + { + mtn_automate::attr_map_t::const_iterator a + = f->second.second.find(i->first.second); + if (a==f->second.second.end()) cs.attrs_set[i->first]=i->second; + else if (a->second!=i->second) + { + cs.attrs_set[i->first]=i->second; + any_change=true; + } + } + } + // deleted attributes + for (mtn_automate::manifest_map::const_iterator i=oldmanifest.begin(); + i!=oldmanifest.end(); ++i) + { + file_path sp=i->first; + for (mtn_automate::attr_map_t::const_iterator a=i->second.second.begin(); + a!=i->second.second.end(); ++a) + { + mtn_automate::sync_map_t::const_iterator f + =state.find(std::make_pair(sp,a->first)); + // we do not have to delete attributes of deleted notes + if (f==state.end() && cs.nodes_deleted.find(sp)==cs.nodes_deleted.end()) + { + cs.attrs_cleared.insert(std::make_pair(sp,a->first)); + any_change=true; + } + } + } + // delete old dummy attribute if present + { mtn_automate::manifest_map::const_iterator f=oldmanifest.find(file_path_internal("")); + if (f!=oldmanifest.end() && f->second.second.find(attr_key(app.opts.domain+":touch"))!=f->second.second.end()) + { cs.attrs_cleared.insert(std::make_pair(file_path_internal(""),attr_key(app.opts.domain+":touch"))); + any_change=true; + } + } + if (!any_change) // this happens if only deletions happened + { cs.attrs_set[std::make_pair(file_path_internal(""),attr_key(app.opts.domain+":touch"))] + =attr_value("synchronized"); + } +} + +static +void add_missing_parents(mtn_automate::manifest_map const& oldr, + file_path const & sp, mtn_automate::cset & cs) +{ + std::vector > components; + L(FL("add_missing_parents(,%s,)\n") % sp); + file_path sub=sp; + do + { + path_component comp; + file_path sub2=sub; + // dirname_basename cannot output to the same object + sub2.dirname_basename(sub,comp); + components.push_back(std::make_pair(sub,comp)); + } while (!sub.empty()); + for (std::vector >::const_reverse_iterator i=components.rbegin(); + i!=static_cast >::const_reverse_iterator>(components.rend()); + ++i) + { L(FL("path comp '%s'\n") % i->first); + // already added? + if (cs.dirs_added.find(i->first)!=cs.dirs_added.end()) continue; + mtn_automate::manifest_map::const_iterator mi=oldr.find(i->first); + if (mi==oldr.end()) + { L(FL("adding directory %s\n") % i->first); + safe_insert(cs.dirs_added, i->first); + } + } +} + +// compare the new manifest with the old roster and fill the cset accordingly +static void +build_change_set(const cvs_client &c, mtn_automate::manifest_map const& oldr, cvs_manifest &newm, + mtn_automate::cset &cs, cvs_file_state const& remove_state) +{ +// cvs_manifest cvs_delta; + +// node_map const & nodes = oldr.all_nodes(); + L(FL("build_change_set(%d,%d,)\n") % oldr.size() % newm.size()); + + for (mtn_automate::manifest_map::const_iterator f = oldr.begin(); f != oldr.end(); ++f) + { if (null_id(f->second.first)) continue; // directory + + cvs_manifest::const_iterator fn = newm.find(f->first.as_internal()); + if (fn==newm.end()) + { + L(FL("deleting file '%s'\n") % f->first); + safe_insert(cs.nodes_deleted, f->first); +// cvs_delta[path.as_internal()]=remove_state; + } + else + { if (f->second.first == fn->second->sha1sum) + { +// L(FL("skipping preserved entry state '%s' on '%s'\n") +// % fn->second->sha1sum % fn->first); + } + else + { + L(FL("applying state delta on '%s' : '%s' -> '%s'\n") + % f->first % f->second.first % fn->second->sha1sum); + I(!fn->second->sha1sum.inner()().empty()); + safe_insert(cs.deltas_applied, std::make_pair(f->first, std::make_pair(f->second.first,fn->second->sha1sum))); +// cvs_delta[path.as_internal()]=fn->second; + } +#warning 2do mode_change ? + // cs.attrs_cleared cs.attrs_set + //if (fn->second.second->?? + } + } + for (cvs_manifest::const_iterator f = newm.begin(); f != newm.end(); ++f) + { mtn_automate::manifest_map::const_iterator mi=oldr.find(file_path_internal(f->first)); + if (mi==oldr.end()) + { + L(FL("adding file '%s' as '%s'\n") % f->second->sha1sum % f->first); + I(!f->second->sha1sum.inner()().empty()); + file_path sp=file_path_internal(f->first); + add_missing_parents(oldr, sp, cs); + safe_insert(cs.files_added, make_pair(sp, f->second->sha1sum)); +// cvs_delta[f->first]=f->second; + if (f->second->mode&0111) + safe_insert(cs.attrs_set, std::make_pair(std::make_pair(sp, attr_key("mtn:execute")), attr_value("true"))); + if (f->second->keyword_substitution=="-kb") + safe_insert(cs.attrs_set, std::make_pair(std::make_pair(sp, attr_key("mtn:manual_merge")), attr_value("true"))); + } + } +} + +static std::string time_t2monotone(const time_t &t) +{ struct tm *tm; + tm=gmtime(&t); + return (boost::format("%04d-%02d-%02dT%02d:%02d:%02d") % (tm->tm_year+1900) + % (tm->tm_mon+1) % tm->tm_mday % tm->tm_hour % tm->tm_min + % tm->tm_sec).str(); +} + +// commit CVS revisions to monotone (pull) +void cvs_repository::commit_cvs2mtn(std::set::iterator e) +{ revision_id parent_rid; + + cvs_edges_ticker.reset(0); + L(FL("commit_revisions(%s %s)\n") % time_t2human(e->time) % e->revision); + revision_ticker.reset(new ticker("revisions", "R", 3)); + if (e!=edges.begin()) + { std::set::const_iterator before=e; + --before; + L(FL("found last committed %s %s\n") % time_t2human(before->time) % before->revision); + I(!before->revision.inner()().empty()); + parent_rid=before->revision; + } +// temp_node_id_source nis; + for (; e!=edges.end(); ++e) + { // roster_t new_roster=old_roster; + // editable_roster_base eros(new_roster,nis); + mtn_automate::cset cs; + I(e->delta_base.inner()().empty()); // no delta yet + cvs_manifest child_manifest=get_files(*e); + L(FL("build_change_set(%s %s)\n") % time_t2human(e->time) % e->revision); + // revision_set rev; + // boost::shared_ptr cs(new cset()); + mtn_automate::manifest_map oldmanifest; + if (!null_id(parent_rid)) + oldmanifest=app.get_manifest_of(parent_rid); + build_change_set(*this,oldmanifest,e->xfiles,cs,remove_state); + attach_sync_state(const_cast(*e),oldmanifest,cs); + //cs->apply_to(eros); + //calculate_ident(new_roster, rev.new_manifest); + //safe_insert(rev.edges, std::make_pair(parent_rid, cs)); + + if (!cs.is_nontrivial()) + { W(F("null edge (empty cs) @%s skipped\n") % time_t2human(e->time)); + continue; + } + if (e->xfiles.empty()) + { W(F("empty edge (no files) @%s skipped\n") % time_t2human(e->time)); + continue; + } +#if 0 + if (child_map.empty()) + { W(F("empty edge (no files in manifest) @%s skipped\n") % time_t2human(e->time)); + // perhaps begin a new tree: + // parent_rid=revision_id(); +// parent_manifest=cvs_manifest(); + continue; + } +#endif + revision_id child_rid=app.put_revision(parent_rid,cs); + if (revision_ticker.get()) ++(*revision_ticker); + L(FL("CVS Sync: Inserted revision %s into repository\n") % child_rid); + e->revision=child_rid; + + app.cert_revision(child_rid,"branch",app.opts.branchname()); + std::string author=e->author; + if (author.find('@')==std::string::npos) author+="@"+host; + app.cert_revision(child_rid, "author", author); + app.cert_revision(child_rid, "changelog", e->changelog); + app.cert_revision(child_rid, "date", time_t2monotone(e->time)); + parent_rid = child_rid; + } +} + +struct cvs_repository::prime_log_cb : rlog_callbacks +{ cvs_repository &repo; + std::map::iterator i; + time_t override_time; + prime_log_cb(cvs_repository &r,const std::map::iterator &_i + ,time_t overr_time=-1) + : repo(r), i(_i), override_time(overr_time) {} + virtual void tag(const std::string &file,const std::string &tag, + const std::string &revision) const; + virtual void revision(const std::string &file,time_t t, + const std::string &rev,const std::string &author, + const std::string &state,const std::string &log) const; + virtual void file(const std::string &file,const std::string &head_rev) const + { } +}; + +void cvs_repository::prime_log_cb::tag(const std::string &file,const std::string &tag, + const std::string &revision) const +{ MM(file); + MM(tag); + I(i->first==file); + std::map &tagslot=repo.tags[tag]; + tagslot[file]=revision; + if (tag==repo.branch) repo.branch_point[file]=cvs_revision_nr(revision).get_branch_root(); +} + +static std::string app_signing_key="address@hidden"; + +void cvs_repository::prime_log_cb::revision(const std::string &file,time_t checkin_time, + const std::string &revision,const std::string &_author, + const std::string &dead,const std::string &_message) const +{ L(FL("prime_log_cb %s:%s %s %s %d %s\n") % file % revision % time_t2human(checkin_time) + % _author % _message.size() % dead); + std::string author=_author; + std::string message=_message; + I(i->first==file); + if (override_time!=-1) + { checkin_time=override_time; + message="initial state for cvs_pull --since"; + author=app_signing_key; + } + std::pair::iterator,bool> iter= + i->second.known_states.insert + (file_state(checkin_time,revision,dead=="dead")); + // I(iter.second==false); + // set iterators are read only to prevent you from destroying the order + file_state &fs=const_cast(*(iter.first)); + fs.log_msg=message; + fs.author=author; + std::pair::iterator,bool> iter2= + repo.edges.insert(cvs_edge(message,checkin_time,author)); + if (iter2.second && repo.cvs_edges_ticker.get()) ++(*repo.cvs_edges_ticker); +} + +void cvs_repository::prime() +{ retrieve_modules(); + get_all_files(); + revision_ticker.reset(0); + cvs_edges_ticker.reset(new ticker("edges", "E", 10)); + for (std::map::iterator i=files.begin();i!=files.end();++i) + { std::vector args; + MM(i->first); + + if (!branch.empty()) + { args.push_back("-r"+branch); + N(sync_since==-1, F("--since does not work on a side branch")); + } + else + args.push_back("-b"); + + if (sync_since!=-1) + { args.push_back("-d"); + size_t date_index=args.size(); + args.push_back(time_t2rfc822(sync_since)); + // state _at_ this point in time + Log(prime_log_cb(*this,i,sync_since),i->first,args); + // -d Jun 20 09:38:29 1997< + args[date_index]+='<'; + // state _since_ this point in time + Log(prime_log_cb(*this,i,sync_since),i->first,args); + } + else + Log(prime_log_cb(*this,i),i->first,args); + } + // remove duplicate states (because some edges were added by the + // get_all_files method) + for (std::set::iterator i=edges.begin();i!=edges.end();) + { if (i->changelog_valid || i->author.size()) { ++i; continue; } + std::set::iterator j=i; + j++; + MM(boost::lexical_cast(i->time)); + MM(boost::lexical_cast(j->time)); + I(j!=edges.end()); + I(j->time==i->time); + I(i->xfiles.empty()); + edges.erase(i); + if (cvs_edges_ticker.get()) --(*cvs_edges_ticker); + i=j; + } + + // join adjacent check ins (same author, same changelog) + join_edge_parts(edges.begin()); + + if (!branch_point.empty()) + { time_t root_time(0); + // FIXME: look for this edge already in the database + if (edges.begin()!=edges.end()) root_time=edges.begin()->time-1; + std::set::iterator root_edge + =edges.insert(cvs_edge(branch+" branching point",root_time,app_signing_key)).first; + for (std::map::const_iterator i=branch_point.begin();i!=branch_point.end();++i) + { file_state fs(root_edge->time,i->second.get_string()); + fs.log_msg=root_edge->changelog; + fs.author=root_edge->author; + files[i->first].known_states.insert(fs); + } + } + // since log already used Entry+Unchanged, reconnect to forget this states + reconnect(); + + // get the contents + for (std::map::iterator i=files.begin();i!=files.end();++i) + { std::string file_contents; + MM(i->first); + I(!branch.empty() || !i->second.known_states.empty()); + if (!i->second.known_states.empty()) + { std::set::iterator s2=i->second.known_states.begin(); + cvs_client::update c=Update(i->first,s2->cvs_version); + store_checkout(s2,c,file_contents); + } + for (std::set::iterator s=i->second.known_states.begin(); + s!=i->second.known_states.end();++s) + { std::set::iterator s2=s; + ++s2; + if (s2==i->second.known_states.end()) break; + update(s,s2,i->first,file_contents); + } + } + drop_connection(); + + // fill in file states at given point + fill_manifests(edges.begin()); + + // commit them all + if (!edges.empty()) commit_cvs2mtn(edges.begin()); + +// store_modules(); +} + +struct cvs_client::checkout cvs_repository::CheckOut2(const std::string &file, const std::string &revision) +{ try + { return CheckOut(file,revision); + } catch (oops &e) + { W(F("trying to reconnect, perhaps the server is confused\n")); + reconnect(); + return CheckOut(file,revision); + } +} + +const cvs_manifest &cvs_repository::get_files(const revision_id &rid) +{ std::map::iterator>::const_iterator + cache_item=revision_lookup.find(rid); + I(cache_item!=revision_lookup.end()); + return get_files(*(cache_item->second)); +} + +const cvs_manifest &cvs_repository::get_files(const cvs_edge &e) +{ L(FL("get_files(%s %s) %s %d\n") % time_t2human(e.time) % e.revision % e.delta_base % e.xfiles.size()); +#if 0 + if (!e.delta_base.inner()().empty()) + { cvs_manifest calculated_manifest; + // this is non-recursive by reason ... + const cvs_edge *current=&e; + std::vector deltas; +#if 0 // no longer delta encoded + while (!current->delta_base.inner()().empty()) + { L(FL("get_files: looking for base rev %s\n") % current->delta_base); +// ++e.cm_delta_depth; + deltas.push_back(current); + std::map::iterator>::const_iterator + cache_item=revision_lookup.find(current->delta_base); + E(cache_item!=revision_lookup.end(), + F("missing cvs cert on base revision %s\n") % current->delta_base); + current=&*(cache_item->second); + } + I(current->delta_base.inner()().empty()); +#endif + calculated_manifest=current->xfiles; + for (std::vector::const_reverse_iterator i=deltas.rbegin(); + i!=static_cast::const_reverse_iterator>(deltas.rend()); + ++i) + apply_manifest_delta(calculated_manifest,(*i)->xfiles); + e.xfiles=calculated_manifest; + e.delta_base=revision_id(); + } +#endif + return e.xfiles; +} + +void cvs_sync::pull(const std::string &_repository, const std::string &_module, + std::string const& _branch, mtncvs_state &app) +{ cvs_repository *repo=prepare_sync(_repository,_module,_branch,app); + + // initial checkout + if (repo->empty()) + repo->prime(); + else repo->update(); + delete repo; +} + +struct cvs_repository::update_cb : cvs_client::update_callbacks +{ cvs_repository &repo; + std::vector &results; + + update_cb(cvs_repository &r, std::vector &re) + : repo(r), results(re) {} + virtual void operator()(const cvs_client::update &u) const + { results.push_back(u); + // perhaps store the file contents into the db to save storage + } +}; + +void cvs_repository::update() +{ + retrieve_modules(); + std::set::iterator now_iter=last_known_revision(); + const cvs_edge &now=*now_iter; + I(!now.revision.inner()().empty()); + std::vector file_revisions; + std::vector results; + const cvs_manifest &m=get_files(now); + file_revisions.reserve(m.size()); +#warning FIXME: changed files + for (cvs_manifest::const_iterator i=m.begin();i!=m.end();++i) + file_revisions.push_back(update_args(i->first,i->second->cvs_version, + std::string(),i->second->keyword_substitution)); + Update(file_revisions,update_cb(*this,results)); + for (std::vector::const_iterator i=results.begin();i!=results.end();++i) + { // 2do: use tags + cvs_manifest::const_iterator now_file=m.find(i->file); + std::string last_known_revision; + std::map::iterator f=files.find(i->file); + + if (now_file!=m.end()) + { last_known_revision=now_file->second->cvs_version; + I(f!=files.end()); + } + else // the file is not present in our last import + // e.g. the file is currently dead but we know an old revision + { if (f!=files.end() // we know anything about this file + && !f->second.known_states.empty()) // we have at least one known file revision + { std::set::const_iterator last=f->second.known_states.end(); + --last; + last_known_revision=last->cvs_version; + } + else f=files.insert(std::make_pair(i->file,file_history())).first; + } + if (last_known_revision=="1.1.1.1") + last_known_revision="1.1"; + std::set::const_iterator last=f->second.known_states.end(); + if (last!=f->second.known_states.begin()) --last; + + if (last_known_revision.empty()) + Log(prime_log_cb(*this,f),i->file.c_str(),"-b","-N",(void*)0); + else + // -b causes -r to get ignored on 0.12 + Log(prime_log_cb(*this,f),i->file.c_str(),/*"-b",*/"-N", + ("-r"+last_known_revision+"::").c_str(),(void*)0); + + std::string file_contents,initial_contents; + if(last==f->second.known_states.end() || last->dead) + { last=f->second.known_states.begin(); + I(last!=f->second.known_states.end()); + std::set::iterator s2=last; + cvs_client::update c=Update(i->file,s2->cvs_version); + store_checkout(s2,c,file_contents); + } + else + { I(!last->sha1sum.inner()().empty()); + file_contents=app.get_file(last->sha1sum).inner()(); + initial_contents=file_contents; + } + for (std::set::const_iterator s=last; + s!=f->second.known_states.end();++s) + { std::set::const_iterator s2=s; + ++s2; + if (s2==f->second.known_states.end()) break; + if (s2->cvs_version==i->new_revision) + { // we do not need to ask the host, we already did ... + try + { store_update(last,s2,*i,initial_contents); + } catch (informative_failure &e) + { W(F("error during update: %s\n") % e.what()); + // we _might_ try to use store delta ... + cvs_client::update c=Update(i->file,s2->cvs_version); + const_cast(s2->md5sum)=""; + const_cast(s2->patchsize)=0; + store_contents(file_data(c.contents), const_cast(s2->sha1sum)); + const_cast(s2->size)=c.contents.size(); + const_cast(s2->keyword_substitution)=c.keyword_substitution; + } + break; + } + else + { update(s,s2,i->file,file_contents); + } + } + } + drop_connection(); + + std::set::iterator dummy_iter=now_iter; + ++dummy_iter; + if (dummy_iter!=edges.end()) + { + join_edge_parts(dummy_iter); + fill_manifests(dummy_iter); + if (global_sanity.debug_p()) L(FL("%s") % debug()); + commit_cvs2mtn(dummy_iter); + } +// store_modules(); +} + ============================================================ --- mtn_cvs/cvs_sync_push.cc 4b19c7dd823bf02e72896eba7cd4a41de43718bf +++ mtn_cvs/cvs_sync_push.cc 4b19c7dd823bf02e72896eba7cd4a41de43718bf @@ -0,0 +1,377 @@ +// copyright (C) 2005-2006 Christof Petig +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#include "cvs_sync.hh" +#include "mtncvs_state.hh" +#include "keys.hh" +#include "transforms.hh" +#include +#include +#include "botan/md5.h" +#include +#include +#include +#include +#include "stringtok.hh" +#include "piece_table.hh" +#include "safe_map.hh" +#include + +#ifdef WIN32 +#define sleep(x) _sleep(x) +#endif + +using namespace std; +using namespace cvs_sync; + +std::string cvs_repository::gather_merge_information(revision_id const& id) +{ L(FL("gather_merge_information(%s)") % id); + std::vector parents=app.get_revision_parents(id); + std::string result; + for (std::vector::const_iterator i=parents.begin();i!=parents.end();++i) + { if (*i==revision_id()) continue; +#if 0 + std::vector::const_iterator j=certs.begin(); + std::string to_match=create_cvs_cert_header(); + for (;j!=certs.end();++j) + { if (j->inner().name()!=cvs_cert_name) continue; + cert_value value; + decode_base64(j->inner().value, value); + if (value().size() certs=app.get_revision_certs(*i); + std::string author,changelog; + time_t date=0; + for (std::vector::const_iterator j=certs.begin();j!=certs.end();++j) + { if (!j->trusted || j->signature!=mtn_automate::certificate::ok) + continue; + if (j->name=="date") + { date=cvs_repository::posix2time_t(j->value); + } + else if (j->name=="author") + { author=j->value; + } + else if (j->name=="changelog") + { changelog=j->value; + } + } + result+="-------------------\n" + +changelog+"\nmonotone "+author+" " + +cvs_client::time_t2rfc822(date)+" "+i->inner()()+"\n"; + result+=gather_merge_information(*i); + } + return result; +} + +void cvs_sync::push(const std::string &_repository, const std::string &_module, + std::string const& _branch, mtncvs_state &app) +{ cvs_repository *repo=cvs_sync::prepare_sync(_repository,_module,_branch,app); + + if (repo->empty()) + W(F("no revision certs for this module, exporting all\n")); + L(FL("push")); +// std::cerr << repo->debug() << '\n'; + repo->commit(); + delete repo; +} + +namespace { +struct is_branch +{ std::string comparison; + is_branch(std::string const& br) : comparison(br) {} + bool operator()(mtn_automate::certificate const& cert) + { return cert.trusted + && cert.signature==mtn_automate::certificate::ok + && cert.name=="branch" + && cert.value==comparison; + } +}; +} + +void cvs_repository::commit() +{ retrieve_modules(); + if (edges.empty()) + { // search for a matching start of history + // take first head + std::vector heads=app.heads(app.opts.branchname()); + N(!heads.empty(), F("branch %s has no heads") % app.opts.branchname()); + + revision_id actual=*heads.begin(); + is_branch branch_comparer(app.opts.branchname()); + do + { std::vector parents=app.get_revision_parents(actual); + for (std::vector::const_iterator i=parents.begin(); + i!=parents.end();++i) + { if (*i==revision_id()) break; // root revision + std::vector certs + = app.get_revision_certs(*i); + for (std::vector::const_iterator j=certs.begin(); + j!=certs.end();++j) + if (branch_comparer(*j)) + { actual=*i; + goto continue_outer; + } + } + continue_outer: ; + } while (true); + // start with actual + I(!null_id(actual)); + bool fail=false; + // set up an empty CVS revision? + commit_mtn2cvs(edges.end(), actual, fail); +// automate::manifest_map root_manifest=app.get_manifest_of(actual); + + I(!fail); + } + std::set::iterator now_iter=last_known_revision(); + while (now_iter!=edges.end()) + { const cvs_edge &now=*now_iter; + I(!now.revision.inner()().empty()); + + L(FL("looking for children of revision %s\n") % now.revision); + std::vector children=app.get_revision_children(now.revision); + + if (!app.opts.branchname().empty()) + { // app.opts.branch_name + // ignore revisions not belonging to the specified branch + for (std::vector::iterator i=children.begin(); + i!=children.end();) + { std::vector certs + = app.get_revision_certs(*i); + if (std::remove_if(certs.begin(),certs.end(),is_branch(app.opts.branchname()))==certs.begin()) + i=children.erase(i); + else ++i; + } + } + if (children.empty()) return; + revision_id next; + if (children.size()>1 && !app.opts.first) + { for (std::vector::const_iterator i=app.opts.revisions.begin(); + i!=app.opts.revisions.end();++i) + { for (std::vector::const_iterator j=children.begin(); + j!=children.end();++j) + { if (*i==*j) + { next=*j; + break; + } + } + } + if (next.inner()().empty()) + { W(F("several children found for %s:\n") % now.revision); + for (std::vector::const_iterator i=children.begin(); + i!=children.end();++i) + { W(F("%s\n") % *i); + } + W(F("please specify direction using --revision\n")); + return; + } + } + else next=*children.begin(); + bool fail=bool(); + now_iter=commit_mtn2cvs(now_iter,next,fail); + + if (!fail) + P(F("checked %s into cvs repository") % now.revision); + // we'd better seperate the commits so that ordering them is possible + if (now_iter!=edges.end()) sleep(2); + } +// store_modules(); +} + +std::set::iterator cvs_repository::commit_mtn2cvs( + std::set::iterator parent, const revision_id &rid, bool &fail) +{ // check that it's the last one + L(FL("commit %s -> %s\n") % parent->revision % rid); + if (parent!=edges.end()) // accept full push + { std::set::iterator test=parent; + ++test; + I(test==edges.end()); + } + // a bit like process_certs + cvs_edge e(rid,app); + + mtn_automate::revision_t rs=app.get_revision(rid); + std::vector commits; + + for (mtn_automate::edge_map::const_iterator j = rs.edges.begin(); + j != rs.edges.end(); + ++j) + { if ((parent==edges.end() && !null_id(j->first))) + { L(FL("%s != \"\"\n") % j->first); + continue; + } + else if ((parent!=edges.end() && !(j->first == parent->revision))) + { L(FL("%s != %s\n") % j->first % (parent->revision)); + continue; + } + boost::shared_ptr cs=j->second; + cvs_manifest parent_manifest; + if (parent!=edges.end()) parent_manifest=get_files(*parent); + std::map renamed_ids; + + for (mtn_automate::path_set::const_iterator i=cs->nodes_deleted.begin(); + i!=cs->nodes_deleted.end(); ++i) + { commit_arg a; + a.file=file_path(*i).as_internal(); + cvs_manifest::const_iterator old=parent_manifest.find(a.file); +// if (a.file==".mtn-sync-"+app.opts.domain) continue; + I(old!=parent_manifest.end()); + a.removed=true; + a.old_revision=old->second->cvs_version; + a.keyword_substitution=old->second->keyword_substitution; + commits.push_back(a); + L(FL("delete %s -%s %s\n") % a.file % a.old_revision % a.keyword_substitution); + } + + for (std::map::const_iterator i + =cs->nodes_renamed.begin(); + i!=cs->nodes_renamed.end(); ++i) + { commit_arg a; // remove + a.file=file_path(i->first).as_internal(); + cvs_manifest::const_iterator old=parent_manifest.find(a.file); + I(old!=parent_manifest.end()); + a.removed=true; + a.old_revision=old->second->cvs_version; + a.keyword_substitution=old->second->keyword_substitution; + commits.push_back(a); + L(FL("rename from %s -%s %s\n") % a.file % a.old_revision % a.keyword_substitution); + + a=commit_arg(); // add + a.file=file_path(i->second).as_internal(); + I(!old->second->sha1sum.inner()().empty()); + std::map >::const_iterator change_ent = + cs->deltas_applied.find(i->second); + if (change_ent != cs->deltas_applied.end()) + // the file content is going to change - handle that little detail now... + renamed_ids[i->second] = change_ent->second.second; + else + renamed_ids[i->second] = old->second->sha1sum; + a.new_content=app.get_file(renamed_ids[i->second]).inner()(); + commits.push_back(a); + L(FL("rename to %s %d\n") % a.file % a.new_content.size()); + } + + for (mtn_automate::path_set::const_iterator i=cs->dirs_added.begin(); + i!=cs->dirs_added.end(); ++i) + { std::string name=file_path(*i).as_internal(); + L(FL("dir add %s\n") % name); + + std::string parent,dir=name; + std::string::size_type last_slash=name.rfind('/'); + if (last_slash!=std::string::npos) + { parent=name.substr(0,last_slash); + dir=name.substr(last_slash+1); + } + AddDirectory(dir,parent); + } + + for (std::map::const_iterator + i=cs->files_added.begin(); + i!=cs->files_added.end(); ++i) + { + commit_arg a; + a.file=file_path(i->first).as_internal(); +// if (a.file==".mtn-sync-"+app.opts.domain) continue; + a.new_content=app.get_file(i->second).inner()(); + commits.push_back(a); + L(FL("add %s %d\n") % a.file % a.new_content.size()); + } + + for (std::map >::const_iterator + i=cs->deltas_applied.begin(); + i!=cs->deltas_applied.end(); ++i) + { + if (renamed_ids.find(i->first) != renamed_ids.end()) + continue; // a renamed file that's already been added with the correct contents + commit_arg a; + a.file=file_path(i->first).as_internal(); +// if (a.file==".mtn-sync-"+app.opts.domain) continue; + cvs_manifest::const_iterator old=parent_manifest.find(a.file); + I(old!=parent_manifest.end()); + a.old_revision=old->second->cvs_version; + a.keyword_substitution=old->second->keyword_substitution; + a.new_content=app.get_file(i->second.second).inner()(); + commits.push_back(a); + L(FL("delta %s %s %s %d\n") % a.file % a.old_revision % a.keyword_substitution + % a.new_content.size()); + } + + if (commits.empty()) + { W(F("revision %s: nothing to commit") % e.revision.inner()()); + if (parent!=edges.end()) e.delta_base=parent->revision; + cert_cvs(e); + revision_lookup[e.revision]=edges.insert(e).first; + fail=false; + return --(edges.end()); + } + std::string changelog; + changelog=e.changelog+"\nmonotone "+e.author+" " + +cvs_client::time_t2rfc822(e.time)+" "+e.revision.inner()().substr(0,6)+"\n"; + // gather information CVS does not know about into the changelog + changelog+=gather_merge_information(e.revision); + std::map > result + =Commit(changelog,e.time,commits); + if (result.empty()) { fail=true; return edges.end(); } + + if (parent!=edges.end()) + e.delta_base=parent->revision; + + // the result of the commit: create history entry (file state) + // FIXME: is this really necessary? + for (std::map >::const_iterator + i=result.begin(); i!=result.end(); ++i) + { if (i->second.first.empty()) + { e.xfiles[i->first]=remove_state; + } + else + { MM(i->first); + file_state fs(e.time,i->second.first); + fs.log_msg=e.changelog; + fs.author=e.author; + fs.keyword_substitution=i->second.second; + file_path sp= file_path_internal(i->first); + std::map >::const_iterator mydelta=cs->deltas_applied.find(sp); + if (mydelta!=cs->deltas_applied.end()) + { fs.sha1sum=mydelta->second.second; + } + else // newly added? + { std::map::const_iterator myadd=cs->files_added.find(sp); + if (myadd!=cs->files_added.end()) + { fs.sha1sum=myadd->second; + } + else // renamed? + { std::map::const_iterator myrename=renamed_ids.find(sp); + I(myrename!=renamed_ids.end()); + fs.sha1sum=myrename->second; + } + } + std::pair::iterator,bool> newelem= + files[i->first].known_states.insert(fs); + I(newelem.second); + e.xfiles[i->first]=newelem.first; + } + } + cert_cvs(e); + revision_lookup[e.revision]=edges.insert(e).first; + if (global_sanity.debug_p()) L(FL("%s") % debug()); + fail=false; + return --(edges.end()); + } + W(F("no matching parent found\n")); + fail=true; + return edges.end(); +} + ============================================================ --- mtn_cvs/cvs_sync_takeover.cc 89ecce5569183386f4e6e686babe821b6ebfca3d +++ mtn_cvs/cvs_sync_takeover.cc 89ecce5569183386f4e6e686babe821b6ebfca3d @@ -0,0 +1,227 @@ +// copyright (C) 2005-2006 Christof Petig +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#include "cvs_sync.hh" +#include "mtncvs_state.hh" +#include "keys.hh" +#include "transforms.hh" +#include +#include +#include "botan/md5.h" +#include +#include +#include +#include +#include "stringtok.hh" +#include "piece_table.hh" +#include "safe_map.hh" +#include + +#ifdef WIN32 +#define sleep(x) _sleep(x) +#endif + +using namespace std; +using namespace cvs_sync; + +static void read_file(std::string const& name, file_data &result) +{ std::string dest; + ifstream is(name.c_str()); + while (is.good()) + { char buf[10240]; + is.read(buf,sizeof buf); + if (is.gcount()) dest+=std::string(buf,buf+is.gcount()); + } + result=file_data(dest); +} + +void cvs_repository::takeover_dir(const std::string &path) +{ // remember the server path for this subdirectory + MM(path); + { std::string repository; + std::ifstream cvs_repository((path+"CVS/Repository").c_str()); + N(cvs_repository.good(), F("can't open %sCVS/Repository\n") % path); + std::getline(cvs_repository,repository); + I(!repository.empty()); + if (repository[0]!='/') repository=root+"/"+repository; + validate_path(path,repository+"/"); + } + std::ifstream cvs_Entries((path+"CVS/Entries").c_str()); + N(cvs_Entries.good(), + F("can't open %s\n") % (path+"CVS/Entries")); + L(FL("takeover_dir %s\n") % path); + static hexenc empty_file; + while (true) + { std::string line; + std::getline(cvs_Entries,line); + if (!cvs_Entries.good()) break; + if (!line.empty()) + { std::vector parts; + MM(line); + stringtok(parts,line,"/"); + // empty last part will not get created + if (parts.size()==5) parts.push_back(std::string()); + if (parts.size()!=6) + { W(F("entry line with %d components '%s'\n") % parts.size() %line); + continue; + } + if (parts[0]=="D") + { takeover_dir(path+parts[1]+"/"); + } + else // file + { // remember permissions, store file contents + I(parts[0].empty()); + std::string filename=path+parts[1]; + I(!access(filename.c_str(),R_OK)); + // parts[2]=version + // parts[3]=date + // parts[4]=keyword subst + // parts[5]='T'tag + time_t modtime=-1; + try + { modtime=cvs_client::Entries2time_t(parts[3]); + } + catch (informative_failure &e) {} + catch (std::exception &e) {} + + I(files.find(filename)==files.end()); + std::map::iterator f + =files.insert(std::make_pair(filename,file_history())).first; + file_state fs(modtime,parts[2]); + fs.author="unknown"; + fs.keyword_substitution=parts[4]; + { struct stat sbuf; + I(!stat(filename.c_str(), &sbuf)); + if (sbuf.st_mtime!=modtime) + { L(FL("modified %s %u %u\n") % filename % modtime % sbuf.st_mtime); + fs.log_msg="partially overwritten content from last update"; + store_contents(file_data(), fs.sha1sum); + f->second.known_states.insert(fs); + + fs.since_when=time(NULL); + fs.cvs_version=std::string(); + } + } + // import the file and check whether it is (un-)changed + fs.log_msg="initial cvs content"; + file_data new_data; + read_file(filename, new_data); + store_contents(new_data, fs.sha1sum); + f->second.known_states.insert(fs); + } + } + } +} + +void cvs_repository::takeover() +{ app.open(); + takeover_dir(""); + + bool need_second=false; + cvs_edge e1,e2; + e1.time=0; + e1.changelog="last cvs update (modified)"; + e1.changelog_valid=true; + e1.author="unknown"; + e2.time=time(NULL); + e2.changelog="cvs takeover"; + e2.changelog_valid=true; + e2.author="unknown"; + for (std::map::const_iterator i=files.begin(); + i!=files.end();++i) + { cvs_file_state first,second; + first=i->second.known_states.begin(); + I(first!=i->second.known_states.end()); + second=first; + ++second; + if (second==i->second.known_states.end()) second=first; + else if (!need_second) need_second=true; + if (e1.timesince_when) e1.time=first->since_when; + e1.xfiles[i->first]=first; + e2.xfiles[i->first]=second; + // at most two states known ! + I((++second)==i->second.known_states.end()); + } + if (!need_second) e1.changelog=e2.changelog; + edges.insert(e1); + if (need_second) + { edges.insert(e2); + } + // commit them all + commit_cvs2mtn(edges.begin()); + + app.close(); + // now we initialize the workspace + // mtn setup . + { std::vector args; + args.push_back(app.opts.mtn_binary); + if (args[0].empty()) args[0]="mtn"; + for (std::vector::const_iterator i=app.opts.mtn_options.begin();i!=app.opts.mtn_options.end();++i) + args.push_back(*i); + args.push_back("--branch"); + args.push_back(app.opts.branchname()); + args.push_back("setup"); + args.push_back("."); + I(args.size()<30); + const char *argv[30]; + unsigned i=0; + for (;i<30 && irevision.inner()().empty()); + of << "format_version \"1\"\n\n" + "new_manifest [0000000000000000000000000000000000000001]\n\n" + "old_revision [" << encode_hexenc((--edges.end())->revision.inner()()) << "]\n"; + } +// like in commit ? +// update_any_attrs(app); +// put_revision_id((--edges.end())->revision); +// maybe_update_inodeprints(app); +// store_modules(); +} + +// read in directory put into db +void cvs_sync::takeover(mtncvs_state &app, const std::string &_module) +{ std::string root,module=_module,branch; + + N(access("_MTN",F_OK),F("Found a _MTN file or directory, already under monotone's control?")); + { fstream cvs_root("CVS/Root"); + N(cvs_root.good(), + F("can't open ./CVS/Root, please change into the working directory\n")); + std::getline(cvs_root,root); + } + { fstream cvs_branch("CVS/Tag"); + if (cvs_branch.good()) + { std::getline(cvs_branch,branch); + MM(branch); + I(!branch.empty()); + I(branch[0]=='T'); + branch.erase(0,1); + } + } + if (module.empty()) + { fstream cvs_repository("CVS/Repository"); + N(cvs_repository.good(), + F("can't open ./CVS/Repository\n")); + std::getline(cvs_repository,module); + W(F("Guessing '%s' as the module name\n") % module); + } +// test_key_availability(app); + cvs_sync::cvs_repository repo(app,root,module,branch,false); + // FIXME? check that directory layout matches the module structure + repo.takeover(); +} +//#endif + ============================================================ --- mtn_cvs/Makefile.am 09bb01b17cbf25fbe3d7744d461b035aef617307 +++ mtn_cvs/Makefile.am ef9c574400c98dbb33e4509bdba2fe165d83af1f @@ -13,7 +13,9 @@ mtn_cvs_SOURCES = options.cc cvs_sync.cc mtn_cvs_SOURCES = options.cc cvs_sync.cc cvs_client.cc mtn_cvs.cc \ mtn_pipe.cc mtn_automate.cc mtncvs_state.cc \ - cvs_revision_nr.cc cvs_edge.cc ../unix/main.cc + cvs_revision_nr.cc cvs_edge.cc ../unix/main.cc \ + cvs_sync_pull.cc cvs_sync_push.cc cvs_sync_takeover.cc + AM_LDFLAGS = -L. -L.. mtn_cvs_LDADD = -lmtn -lplatform -l3rdparty $(BOOSTLIBS) $(LIBICONV) $(LIBINTL) ============================================================ --- mtn_cvs/cvs_sync.cc e3ca21e430e1de41b51a217b1f9e03eb92eac5f0 +++ mtn_cvs/cvs_sync.cc b55447689889185eb3ecb009847b2bae65c887b7 @@ -23,6 +23,9 @@ #define sleep(x) _sleep(x) #endif +using namespace std; +using namespace cvs_sync; + template B const_map_access(std::map const& m, A const& a) { typename std::map::const_iterator i=m.find(a); @@ -30,13 +33,9 @@ template else return B(); } -using namespace std; - // -> investigate under which conditions a string gets copied //static std::string const cvs_cert_name="cvs-revisions"; -using namespace cvs_sync; - bool file_state::operator<(const file_state &b) const { return since_whentm_sec).str(); } -std::string time_t2monotone(const time_t &t) -{ struct tm *tm; - tm=gmtime(&t); - return (boost::format("%04d-%02d-%02dT%02d:%02d:%02d") % (tm->tm_year+1900) - % (tm->tm_mon+1) % tm->tm_mday % tm->tm_hour % tm->tm_min - % tm->tm_sec).str(); -} - struct cvs_repository::get_all_files_log_cb : rlog_callbacks { cvs_repository &repo; get_all_files_log_cb(cvs_repository &r) : repo(r) {} @@ -231,317 +222,6 @@ std::string cvs_repository::debug() cons return result; } -struct cvs_repository::prime_log_cb : rlog_callbacks -{ cvs_repository &repo; - std::map::iterator i; - time_t override_time; - prime_log_cb(cvs_repository &r,const std::map::iterator &_i - ,time_t overr_time=-1) - : repo(r), i(_i), override_time(overr_time) {} - virtual void tag(const std::string &file,const std::string &tag, - const std::string &revision) const; - virtual void revision(const std::string &file,time_t t, - const std::string &rev,const std::string &author, - const std::string &state,const std::string &log) const; - virtual void file(const std::string &file,const std::string &head_rev) const - { } -}; - -void cvs_repository::prime_log_cb::tag(const std::string &file,const std::string &tag, - const std::string &revision) const -{ MM(file); - MM(tag); - I(i->first==file); - std::map &tagslot=repo.tags[tag]; - tagslot[file]=revision; - if (tag==repo.branch) repo.branch_point[file]=cvs_revision_nr(revision).get_branch_root(); -} - -static std::string app_signing_key="address@hidden"; - -void cvs_repository::prime_log_cb::revision(const std::string &file,time_t checkin_time, - const std::string &revision,const std::string &_author, - const std::string &dead,const std::string &_message) const -{ L(FL("prime_log_cb %s:%s %s %s %d %s\n") % file % revision % time_t2human(checkin_time) - % _author % _message.size() % dead); - std::string author=_author; - std::string message=_message; - I(i->first==file); - if (override_time!=-1) - { checkin_time=override_time; - message="initial state for cvs_pull --since"; - author=app_signing_key; - } - std::pair::iterator,bool> iter= - i->second.known_states.insert - (file_state(checkin_time,revision,dead=="dead")); - // I(iter.second==false); - // set iterators are read only to prevent you from destroying the order - file_state &fs=const_cast(*(iter.first)); - fs.log_msg=message; - fs.author=author; - std::pair::iterator,bool> iter2= - repo.edges.insert(cvs_edge(message,checkin_time,author)); - if (iter2.second && repo.cvs_edges_ticker.get()) ++(*repo.cvs_edges_ticker); -} - -void cvs_repository::store_contents(file_data const &dat, file_id &sha1sum) -{ - if (file_id_ticker.get()) ++(*file_id_ticker); - sha1sum=app.put_file(dat); -} - -static void apply_delta(piece::piece_table &contents, const std::string &patch) -{ piece::piece_table after; - piece::apply_diff(contents,after,patch); - std::swap(contents,after); -} - -void cvs_repository::store_delta(file_data const& new_contents, - file_data const& old_contents, - file_id const&from, file_id &to) -{ if (old_contents.inner()().empty()) - { store_contents(new_contents, to); - return; - } - if (file_id_ticker.get()) ++(*file_id_ticker); - to=app.put_file(new_contents,from); -} - -static -void add_missing_parents(mtn_automate::manifest_map const& oldr, - file_path const & sp, mtn_automate::cset & cs) -{ - std::vector > components; - L(FL("add_missing_parents(,%s,)\n") % sp); - file_path sub=sp; - do - { - path_component comp; - file_path sub2=sub; - // dirname_basename cannot output to the same object - sub2.dirname_basename(sub,comp); - components.push_back(std::make_pair(sub,comp)); - } while (!sub.empty()); - for (std::vector >::const_reverse_iterator i=components.rbegin(); - i!=static_cast >::const_reverse_iterator>(components.rend()); - ++i) - { L(FL("path comp '%s'\n") % i->first); - // already added? - if (cs.dirs_added.find(i->first)!=cs.dirs_added.end()) continue; - mtn_automate::manifest_map::const_iterator mi=oldr.find(i->first); - if (mi==oldr.end()) - { L(FL("adding directory %s\n") % i->first); - safe_insert(cs.dirs_added, i->first); - } - } -} - -// compare the new manifest with the old roster and fill the cset accordingly -static void -build_change_set(const cvs_client &c, mtn_automate::manifest_map const& oldr, cvs_manifest &newm, - mtn_automate::cset &cs, cvs_file_state const& remove_state) -{ -// cvs_manifest cvs_delta; - -// node_map const & nodes = oldr.all_nodes(); - L(FL("build_change_set(%d,%d,)\n") % oldr.size() % newm.size()); - - for (mtn_automate::manifest_map::const_iterator f = oldr.begin(); f != oldr.end(); ++f) - { if (null_id(f->second.first)) continue; // directory - - cvs_manifest::const_iterator fn = newm.find(f->first.as_internal()); - if (fn==newm.end()) - { - L(FL("deleting file '%s'\n") % f->first); - safe_insert(cs.nodes_deleted, f->first); -// cvs_delta[path.as_internal()]=remove_state; - } - else - { if (f->second.first == fn->second->sha1sum) - { -// L(FL("skipping preserved entry state '%s' on '%s'\n") -// % fn->second->sha1sum % fn->first); - } - else - { - L(FL("applying state delta on '%s' : '%s' -> '%s'\n") - % f->first % f->second.first % fn->second->sha1sum); - I(!fn->second->sha1sum.inner()().empty()); - safe_insert(cs.deltas_applied, std::make_pair(f->first, std::make_pair(f->second.first,fn->second->sha1sum))); -// cvs_delta[path.as_internal()]=fn->second; - } -#warning 2do mode_change ? - // cs.attrs_cleared cs.attrs_set - //if (fn->second.second->?? - } - } - for (cvs_manifest::const_iterator f = newm.begin(); f != newm.end(); ++f) - { mtn_automate::manifest_map::const_iterator mi=oldr.find(file_path_internal(f->first)); - if (mi==oldr.end()) - { - L(FL("adding file '%s' as '%s'\n") % f->second->sha1sum % f->first); - I(!f->second->sha1sum.inner()().empty()); - file_path sp=file_path_internal(f->first); - add_missing_parents(oldr, sp, cs); - safe_insert(cs.files_added, make_pair(sp, f->second->sha1sum)); -// cvs_delta[f->first]=f->second; - if (f->second->mode&0111) - safe_insert(cs.attrs_set, std::make_pair(std::make_pair(sp, attr_key("mtn:execute")), attr_value("true"))); - if (f->second->keyword_substitution=="-kb") - safe_insert(cs.attrs_set, std::make_pair(std::make_pair(sp, attr_key("mtn:manual_merge")), attr_value("true"))); - } - } -} - -void cvs_repository::store_update(std::set::const_iterator s, - std::set::iterator s2,const cvs_client::update &u, - std::string &contents) -{ - if (u.removed) - { const_cast(s2->dead)=true; - } - else if (!u.checksum.empty()) - { // const_cast(s2->rcs_patch)=u.patch; - const_cast(s2->md5sum)=u.checksum; - const_cast(s2->patchsize)=u.patch.size(); - const_cast(s2->keyword_substitution)=u.keyword_substitution; - // I(s2->since_when==u.mod_time); - if (u.mod_time!=s2->since_when && u.mod_time!=-1) - { W(F("update time %s and log time %s disagree\n") % time_t2human(u.mod_time) % time_t2human(s2->since_when)); - } - std::string old_contents=contents; - { piece::piece_table file_contents; - piece::index_deltatext(contents,file_contents); - apply_delta(file_contents, u.patch); - piece::build_string(file_contents, contents); - piece::reset(); - } - // check md5 - Botan::MD5 hash; - std::string md5sum=xform(u.checksum); - I(md5sum.size()==hash.OUTPUT_LENGTH); - Botan::SecureVector hashval=hash.process(contents); - I(hashval.size()==hash.OUTPUT_LENGTH); - unsigned hashidx=hash.OUTPUT_LENGTH; - for (;hashidx && hashval[hashidx-1]==Botan::byte(md5sum[hashidx-1]);--hashidx) ; - if (!hashidx) - { store_delta(file_data(contents), file_data(old_contents), s->sha1sum, - const_cast(s2->sha1sum)); - } - else - { E(false, F("MD5 sum %s<>%s") % u.checksum - % xform(std::string(hashval.begin(),hashval.end()))); - } - } - else - { if (!s->sha1sum.inner()().empty()) - // we default to patch if it's at all possible - store_delta(file_data(u.contents), file_data(contents), s->sha1sum, const_cast(s2->sha1sum)); - else - store_contents(file_data(u.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=u.contents.size(); - contents=u.contents; - const_cast(s2->keyword_substitution)=u.keyword_substitution; - } -} - -// s2 gets changed -void cvs_repository::update(std::set::const_iterator s, - std::set::iterator s2,const std::string &file, - std::string &contents) -{ - cvs_revision_nr srev(s->cvs_version); - MM(file); - MM(s->cvs_version); - MM(s2->cvs_version); - if (!srev.is_parent_of(s2->cvs_version)) - std::cerr << "Inconsistency "<< file << ": " << s->cvs_version - << "->" << s2->cvs_version << "\n" << debug() << '\n'; - I(srev.is_parent_of(s2->cvs_version)); - if (s->dead) - { - // this might fail (?) because we issued an Entry somewhere above - // but ... we can specify the correct directory! - cvs_client::update c=Update(file,s2->cvs_version); - I(!c.removed); // dead->dead is no change, so shouldn't get a number - I(!s2->dead); - // I(s2->since_when==c.mod_time); - if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) - { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); - } - store_contents(file_data(c.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=c.contents.size(); - contents=c.contents; - const_cast(s2->keyword_substitution)=c.keyword_substitution; - } - else if (s2->dead) // short circuit if we already know it's dead - { L(FL("file %s: revision %s already known to be dead\n") % file % s2->cvs_version); - } - else - { cvs_client::update u=Update(file,s->cvs_version,s2->cvs_version,s->keyword_substitution); - try - { store_update(s,s2,u,contents); - } catch (informative_failure &e) - { W(F("Update: patching failed with %s\n") % e.what()); - cvs_client::update c=Update(file,s2->cvs_version); - if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) - { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); - } - const_cast(s2->md5sum)=""; - const_cast(s2->patchsize)=0; - store_contents(file_data(c.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=c.contents.size(); - contents=c.contents; - const_cast(s2->keyword_substitution)=c.keyword_substitution; - } catch (std::exception &e) - { W(F("Update: patching failed with %s\n") % e.what()); - cvs_client::update c=Update(file,s2->cvs_version); - if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) - { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); - } - const_cast(s2->md5sum)=""; - const_cast(s2->patchsize)=0; - store_contents(file_data(c.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=c.contents.size(); - contents=c.contents; - const_cast(s2->keyword_substitution)=c.keyword_substitution; - } - } -} - -void cvs_repository::store_checkout(std::set::iterator s2, - const cvs_client::checkout &c, std::string &file_contents) -{ const_cast(s2->dead)=c.dead; - if (!c.dead) - { // I(c.mod_time==s2->since_when); - if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) - { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); - } - store_contents(file_data(c.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=c.contents.size(); - file_contents=c.contents; - const_cast(s2->keyword_substitution)=c.keyword_substitution; - const_cast(s2->mode)=c.mode; - } -} - -void cvs_repository::store_checkout(std::set::iterator s2, - const cvs_client::update &c, std::string &file_contents) -{ const_cast(s2->dead)=c.removed; - if (!c.removed) - { // I(c.mod_time==s2->since_when); - if (c.mod_time!=s2->since_when && c.mod_time!=-1 && s2->since_when!=sync_since) - { W(F("checkout time %s and log time %s disagree\n") % time_t2human(c.mod_time) % time_t2human(s2->since_when)); - } - store_contents(file_data(c.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=c.contents.size(); - file_contents=c.contents; - const_cast(s2->keyword_substitution)=c.keyword_substitution; - const_cast(s2->mode)=c.mode; - } -} - void cvs_repository::fill_manifests(std::set::iterator e) { cvs_manifest current_manifest; if (e!=edges.begin()) @@ -611,67 +291,6 @@ void cvs_repository::fill_manifests(std: } } -void cvs_repository::attach_sync_state(cvs_edge & e,mtn_automate::manifest_map const& oldmanifest, - mtn_automate::cset &cs) -{ mtn_automate::sync_map_t state=create_sync_state(e); - bool any_change=false; - // added and changed attributes - for (mtn_automate::sync_map_t::const_iterator i=state.begin(); - i!=state.end(); ++i) - { - mtn_automate::manifest_map::const_iterator f - = oldmanifest.find(file_path(i->first.first)); - if (f==oldmanifest.end()) - { // only add attributes on existing nodes - if (cs.dirs_added.find(i->first.first)!=cs.dirs_added.end() - || cs.files_added.find(i->first.first)!=cs.files_added.end()) - { cs.attrs_set[i->first]=i->second; - any_change=true; - } - } - else - { - mtn_automate::attr_map_t::const_iterator a - = f->second.second.find(i->first.second); - if (a==f->second.second.end()) cs.attrs_set[i->first]=i->second; - else if (a->second!=i->second) - { - cs.attrs_set[i->first]=i->second; - any_change=true; - } - } - } - // deleted attributes - for (mtn_automate::manifest_map::const_iterator i=oldmanifest.begin(); - i!=oldmanifest.end(); ++i) - { - file_path sp=i->first; - for (mtn_automate::attr_map_t::const_iterator a=i->second.second.begin(); - a!=i->second.second.end(); ++a) - { - mtn_automate::sync_map_t::const_iterator f - =state.find(std::make_pair(sp,a->first)); - // we do not have to delete attributes of deleted notes - if (f==state.end() && cs.nodes_deleted.find(sp)==cs.nodes_deleted.end()) - { - cs.attrs_cleared.insert(std::make_pair(sp,a->first)); - any_change=true; - } - } - } - // delete old dummy attribute if present - { mtn_automate::manifest_map::const_iterator f=oldmanifest.find(file_path_internal("")); - if (f!=oldmanifest.end() && f->second.second.find(attr_key(app.opts.domain+":touch"))!=f->second.second.end()) - { cs.attrs_cleared.insert(std::make_pair(file_path_internal(""),attr_key(app.opts.domain+":touch"))); - any_change=true; - } - } - if (!any_change) // this happens if only deletions happened - { cs.attrs_set[std::make_pair(file_path_internal(""),attr_key(app.opts.domain+":touch"))] - =attr_value("synchronized"); - } -} - mtn_automate::sync_map_t cvs_repository::create_sync_state(cvs_edge const& e) { mtn_automate::sync_map_t state=create_cvs_cert_header(); const std::map &sd=GetServerDir(); @@ -718,165 +337,6 @@ mtn_automate::sync_map_t cvs_repository: return state; } -// commit CVS revisions to monotone (pull) -void cvs_repository::commit_cvs2mtn(std::set::iterator e) -{ revision_id parent_rid; - - cvs_edges_ticker.reset(0); - L(FL("commit_revisions(%s %s)\n") % time_t2human(e->time) % e->revision); - revision_ticker.reset(new ticker("revisions", "R", 3)); - if (e!=edges.begin()) - { std::set::const_iterator before=e; - --before; - L(FL("found last committed %s %s\n") % time_t2human(before->time) % before->revision); - I(!before->revision.inner()().empty()); - parent_rid=before->revision; - } -// temp_node_id_source nis; - for (; e!=edges.end(); ++e) - { // roster_t new_roster=old_roster; - // editable_roster_base eros(new_roster,nis); - mtn_automate::cset cs; - I(e->delta_base.inner()().empty()); // no delta yet - cvs_manifest child_manifest=get_files(*e); - L(FL("build_change_set(%s %s)\n") % time_t2human(e->time) % e->revision); - // revision_set rev; - // boost::shared_ptr cs(new cset()); - mtn_automate::manifest_map oldmanifest; - if (!null_id(parent_rid)) - oldmanifest=app.get_manifest_of(parent_rid); - build_change_set(*this,oldmanifest,e->xfiles,cs,remove_state); - attach_sync_state(const_cast(*e),oldmanifest,cs); - //cs->apply_to(eros); - //calculate_ident(new_roster, rev.new_manifest); - //safe_insert(rev.edges, std::make_pair(parent_rid, cs)); - - if (!cs.is_nontrivial()) - { W(F("null edge (empty cs) @%s skipped\n") % time_t2human(e->time)); - continue; - } - if (e->xfiles.empty()) - { W(F("empty edge (no files) @%s skipped\n") % time_t2human(e->time)); - continue; - } -#if 0 - if (child_map.empty()) - { W(F("empty edge (no files in manifest) @%s skipped\n") % time_t2human(e->time)); - // perhaps begin a new tree: - // parent_rid=revision_id(); -// parent_manifest=cvs_manifest(); - continue; - } -#endif - revision_id child_rid=app.put_revision(parent_rid,cs); - if (revision_ticker.get()) ++(*revision_ticker); - L(FL("CVS Sync: Inserted revision %s into repository\n") % child_rid); - e->revision=child_rid; - - app.cert_revision(child_rid,"branch",app.opts.branchname()); - std::string author=e->author; - if (author.find('@')==std::string::npos) author+="@"+host; - app.cert_revision(child_rid, "author", author); - app.cert_revision(child_rid, "changelog", e->changelog); - app.cert_revision(child_rid, "date", time_t2monotone(e->time)); - parent_rid = child_rid; - } -} - -void cvs_repository::prime() -{ retrieve_modules(); - get_all_files(); - revision_ticker.reset(0); - cvs_edges_ticker.reset(new ticker("edges", "E", 10)); - for (std::map::iterator i=files.begin();i!=files.end();++i) - { std::vector args; - MM(i->first); - - if (!branch.empty()) - { args.push_back("-r"+branch); - N(sync_since==-1, F("--since does not work on a side branch")); - } - else - args.push_back("-b"); - - if (sync_since!=-1) - { args.push_back("-d"); - size_t date_index=args.size(); - args.push_back(time_t2rfc822(sync_since)); - // state _at_ this point in time - Log(prime_log_cb(*this,i,sync_since),i->first,args); - // -d Jun 20 09:38:29 1997< - args[date_index]+='<'; - // state _since_ this point in time - Log(prime_log_cb(*this,i,sync_since),i->first,args); - } - else - Log(prime_log_cb(*this,i),i->first,args); - } - // remove duplicate states (because some edges were added by the - // get_all_files method) - for (std::set::iterator i=edges.begin();i!=edges.end();) - { if (i->changelog_valid || i->author.size()) { ++i; continue; } - std::set::iterator j=i; - j++; - MM(boost::lexical_cast(i->time)); - MM(boost::lexical_cast(j->time)); - I(j!=edges.end()); - I(j->time==i->time); - I(i->xfiles.empty()); - edges.erase(i); - if (cvs_edges_ticker.get()) --(*cvs_edges_ticker); - i=j; - } - - // join adjacent check ins (same author, same changelog) - join_edge_parts(edges.begin()); - - if (!branch_point.empty()) - { time_t root_time(0); - // FIXME: look for this edge already in the database - if (edges.begin()!=edges.end()) root_time=edges.begin()->time-1; - std::set::iterator root_edge - =edges.insert(cvs_edge(branch+" branching point",root_time,app_signing_key)).first; - for (std::map::const_iterator i=branch_point.begin();i!=branch_point.end();++i) - { file_state fs(root_edge->time,i->second.get_string()); - fs.log_msg=root_edge->changelog; - fs.author=root_edge->author; - files[i->first].known_states.insert(fs); - } - } - // since log already used Entry+Unchanged, reconnect to forget this states - reconnect(); - - // get the contents - for (std::map::iterator i=files.begin();i!=files.end();++i) - { std::string file_contents; - MM(i->first); - I(!branch.empty() || !i->second.known_states.empty()); - if (!i->second.known_states.empty()) - { std::set::iterator s2=i->second.known_states.begin(); - cvs_client::update c=Update(i->first,s2->cvs_version); - store_checkout(s2,c,file_contents); - } - for (std::set::iterator s=i->second.known_states.begin(); - s!=i->second.known_states.end();++s) - { std::set::iterator s2=s; - ++s2; - if (s2==i->second.known_states.end()) break; - update(s,s2,i->first,file_contents); - } - } - drop_connection(); - - // fill in file states at given point - fill_manifests(edges.begin()); - - // commit them all - if (!edges.empty()) commit_cvs2mtn(edges.begin()); - -// store_modules(); -} - cvs_repository::cvs_repository(mtncvs_state &_app, const std::string &repository, const std::string &module, const std::string &branch, bool connect) : cvs_client(repository,module,branch,connect), app(_app), @@ -910,343 +370,6 @@ time_t cvs_repository::posix2time_t(std: return dur.total_seconds(); } -std::set::iterator cvs_repository::commit_mtn2cvs( - std::set::iterator parent, const revision_id &rid, bool &fail) -{ // check that it's the last one - L(FL("commit %s -> %s\n") % parent->revision % rid); - if (parent!=edges.end()) // accept full push - { std::set::iterator test=parent; - ++test; - I(test==edges.end()); - } - // a bit like process_certs - cvs_edge e(rid,app); - - mtn_automate::revision_t rs=app.get_revision(rid); - std::vector commits; - - for (mtn_automate::edge_map::const_iterator j = rs.edges.begin(); - j != rs.edges.end(); - ++j) - { if ((parent==edges.end() && !null_id(j->first))) - { L(FL("%s != \"\"\n") % j->first); - continue; - } - else if ((parent!=edges.end() && !(j->first == parent->revision))) - { L(FL("%s != %s\n") % j->first % (parent->revision)); - continue; - } - boost::shared_ptr cs=j->second; - cvs_manifest parent_manifest; - if (parent!=edges.end()) parent_manifest=get_files(*parent); - std::map renamed_ids; - - for (mtn_automate::path_set::const_iterator i=cs->nodes_deleted.begin(); - i!=cs->nodes_deleted.end(); ++i) - { commit_arg a; - a.file=file_path(*i).as_internal(); - cvs_manifest::const_iterator old=parent_manifest.find(a.file); -// if (a.file==".mtn-sync-"+app.opts.domain) continue; - I(old!=parent_manifest.end()); - a.removed=true; - a.old_revision=old->second->cvs_version; - a.keyword_substitution=old->second->keyword_substitution; - commits.push_back(a); - L(FL("delete %s -%s %s\n") % a.file % a.old_revision % a.keyword_substitution); - } - - for (std::map::const_iterator i - =cs->nodes_renamed.begin(); - i!=cs->nodes_renamed.end(); ++i) - { commit_arg a; // remove - a.file=file_path(i->first).as_internal(); - cvs_manifest::const_iterator old=parent_manifest.find(a.file); - I(old!=parent_manifest.end()); - a.removed=true; - a.old_revision=old->second->cvs_version; - a.keyword_substitution=old->second->keyword_substitution; - commits.push_back(a); - L(FL("rename from %s -%s %s\n") % a.file % a.old_revision % a.keyword_substitution); - - a=commit_arg(); // add - a.file=file_path(i->second).as_internal(); - I(!old->second->sha1sum.inner()().empty()); - std::map >::const_iterator change_ent = - cs->deltas_applied.find(i->second); - if (change_ent != cs->deltas_applied.end()) - // the file content is going to change - handle that little detail now... - renamed_ids[i->second] = change_ent->second.second; - else - renamed_ids[i->second] = old->second->sha1sum; - a.new_content=app.get_file(renamed_ids[i->second]).inner()(); - commits.push_back(a); - L(FL("rename to %s %d\n") % a.file % a.new_content.size()); - } - - for (mtn_automate::path_set::const_iterator i=cs->dirs_added.begin(); - i!=cs->dirs_added.end(); ++i) - { std::string name=file_path(*i).as_internal(); - L(FL("dir add %s\n") % name); - - std::string parent,dir=name; - std::string::size_type last_slash=name.rfind('/'); - if (last_slash!=std::string::npos) - { parent=name.substr(0,last_slash); - dir=name.substr(last_slash+1); - } - AddDirectory(dir,parent); - } - - for (std::map::const_iterator - i=cs->files_added.begin(); - i!=cs->files_added.end(); ++i) - { - commit_arg a; - a.file=file_path(i->first).as_internal(); -// if (a.file==".mtn-sync-"+app.opts.domain) continue; - a.new_content=app.get_file(i->second).inner()(); - commits.push_back(a); - L(FL("add %s %d\n") % a.file % a.new_content.size()); - } - - for (std::map >::const_iterator - i=cs->deltas_applied.begin(); - i!=cs->deltas_applied.end(); ++i) - { - if (renamed_ids.find(i->first) != renamed_ids.end()) - continue; // a renamed file that's already been added with the correct contents - commit_arg a; - a.file=file_path(i->first).as_internal(); -// if (a.file==".mtn-sync-"+app.opts.domain) continue; - cvs_manifest::const_iterator old=parent_manifest.find(a.file); - I(old!=parent_manifest.end()); - a.old_revision=old->second->cvs_version; - a.keyword_substitution=old->second->keyword_substitution; - a.new_content=app.get_file(i->second.second).inner()(); - commits.push_back(a); - L(FL("delta %s %s %s %d\n") % a.file % a.old_revision % a.keyword_substitution - % a.new_content.size()); - } - - if (commits.empty()) - { W(F("revision %s: nothing to commit") % e.revision.inner()()); - if (parent!=edges.end()) e.delta_base=parent->revision; - cert_cvs(e); - revision_lookup[e.revision]=edges.insert(e).first; - fail=false; - return --(edges.end()); - } - std::string changelog; - changelog=e.changelog+"\nmonotone "+e.author+" " - +cvs_client::time_t2rfc822(e.time)+" "+e.revision.inner()().substr(0,6)+"\n"; - // gather information CVS does not know about into the changelog - changelog+=gather_merge_information(e.revision); - std::map > result - =Commit(changelog,e.time,commits); - if (result.empty()) { fail=true; return edges.end(); } - - if (parent!=edges.end()) - e.delta_base=parent->revision; - - // the result of the commit: create history entry (file state) - // FIXME: is this really necessary? - for (std::map >::const_iterator - i=result.begin(); i!=result.end(); ++i) - { if (i->second.first.empty()) - { e.xfiles[i->first]=remove_state; - } - else - { MM(i->first); - file_state fs(e.time,i->second.first); - fs.log_msg=e.changelog; - fs.author=e.author; - fs.keyword_substitution=i->second.second; - file_path sp= file_path_internal(i->first); - std::map >::const_iterator mydelta=cs->deltas_applied.find(sp); - if (mydelta!=cs->deltas_applied.end()) - { fs.sha1sum=mydelta->second.second; - } - else // newly added? - { std::map::const_iterator myadd=cs->files_added.find(sp); - if (myadd!=cs->files_added.end()) - { fs.sha1sum=myadd->second; - } - else // renamed? - { std::map::const_iterator myrename=renamed_ids.find(sp); - I(myrename!=renamed_ids.end()); - fs.sha1sum=myrename->second; - } - } - std::pair::iterator,bool> newelem= - files[i->first].known_states.insert(fs); - I(newelem.second); - e.xfiles[i->first]=newelem.first; - } - } - cert_cvs(e); - revision_lookup[e.revision]=edges.insert(e).first; - if (global_sanity.debug_p()) L(FL("%s") % debug()); - fail=false; - return --(edges.end()); - } - W(F("no matching parent found\n")); - fail=true; - return edges.end(); -} - -std::string cvs_repository::gather_merge_information(revision_id const& id) -{ L(FL("gather_merge_information(%s)") % id); - std::vector parents=app.get_revision_parents(id); - std::string result; - for (std::vector::const_iterator i=parents.begin();i!=parents.end();++i) - { if (*i==revision_id()) continue; -#if 0 - std::vector::const_iterator j=certs.begin(); - std::string to_match=create_cvs_cert_header(); - for (;j!=certs.end();++j) - { if (j->inner().name()!=cvs_cert_name) continue; - cert_value value; - decode_base64(j->inner().value, value); - if (value().size() certs=app.get_revision_certs(*i); - std::string author,changelog; - time_t date=0; - for (std::vector::const_iterator j=certs.begin();j!=certs.end();++j) - { if (!j->trusted || j->signature!=mtn_automate::certificate::ok) - continue; - if (j->name=="date") - { date=cvs_repository::posix2time_t(j->value); - } - else if (j->name=="author") - { author=j->value; - } - else if (j->name=="changelog") - { changelog=j->value; - } - } - result+="-------------------\n" - +changelog+"\nmonotone "+author+" " - +cvs_client::time_t2rfc822(date)+" "+i->inner()()+"\n"; - result+=gather_merge_information(*i); - } - return result; -} - -namespace { -struct is_branch -{ std::string comparison; - is_branch(std::string const& br) : comparison(br) {} - bool operator()(mtn_automate::certificate const& cert) - { return cert.trusted - && cert.signature==mtn_automate::certificate::ok - && cert.name=="branch" - && cert.value==comparison; - } -}; -} - -void cvs_repository::commit() -{ retrieve_modules(); - if (edges.empty()) - { // search for a matching start of history - // take first head - std::vector heads=app.heads(app.opts.branchname()); - N(!heads.empty(), F("branch %s has no heads") % app.opts.branchname()); - - revision_id actual=*heads.begin(); - is_branch branch_comparer(app.opts.branchname()); - do - { std::vector parents=app.get_revision_parents(actual); - for (std::vector::const_iterator i=parents.begin(); - i!=parents.end();++i) - { if (*i==revision_id()) break; // root revision - std::vector certs - = app.get_revision_certs(*i); - for (std::vector::const_iterator j=certs.begin(); - j!=certs.end();++j) - if (branch_comparer(*j)) - { actual=*i; - goto continue_outer; - } - } - continue_outer: ; - } while (true); - // start with actual - I(!null_id(actual)); - bool fail=false; - // set up an empty CVS revision? - commit_mtn2cvs(edges.end(), actual, fail); -// automate::manifest_map root_manifest=app.get_manifest_of(actual); - - I(!fail); - } - std::set::iterator now_iter=last_known_revision(); - while (now_iter!=edges.end()) - { const cvs_edge &now=*now_iter; - I(!now.revision.inner()().empty()); - - L(FL("looking for children of revision %s\n") % now.revision); - std::vector children=app.get_revision_children(now.revision); - - if (!app.opts.branchname().empty()) - { // app.opts.branch_name - // ignore revisions not belonging to the specified branch - for (std::vector::iterator i=children.begin(); - i!=children.end();) - { std::vector certs - = app.get_revision_certs(*i); - if (std::remove_if(certs.begin(),certs.end(),is_branch(app.opts.branchname()))==certs.begin()) - i=children.erase(i); - else ++i; - } - } - if (children.empty()) return; - revision_id next; - if (children.size()>1 && !app.opts.first) - { for (std::vector::const_iterator i=app.opts.revisions.begin(); - i!=app.opts.revisions.end();++i) - { for (std::vector::const_iterator j=children.begin(); - j!=children.end();++j) - { if (*i==*j) - { next=*j; - break; - } - } - } - if (next.inner()().empty()) - { W(F("several children found for %s:\n") % now.revision); - for (std::vector::const_iterator i=children.begin(); - i!=children.end();++i) - { W(F("%s\n") % *i); - } - W(F("please specify direction using --revision\n")); - return; - } - } - else next=*children.begin(); - bool fail=bool(); - now_iter=commit_mtn2cvs(now_iter,next,fail); - - if (!fail) - P(F("checked %s into cvs repository") % now.revision); - // we'd better seperate the commits so that ordering them is possible - if (now_iter!=edges.end()) sleep(2); - } -// store_modules(); -} - // look for last sync cert in the given monotone branch and assign // its value to repository, module, branch @@ -1273,24 +396,7 @@ static void guess_repository(std::string } } -namespace cvs_sync -{ -cvs_sync::cvs_repository *prepare_sync(const std::string &_repository, const std::string &_module, - std::string const& _branch, mtncvs_state &app); -} - -void cvs_sync::push(const std::string &_repository, const std::string &_module, - std::string const& _branch, mtncvs_state &app) -{ cvs_repository *repo=cvs_sync::prepare_sync(_repository,_module,_branch,app); - - if (repo->empty()) - W(F("no revision certs for this module, exporting all\n")); - L(FL("push")); -// std::cerr << repo->debug() << '\n'; - repo->commit(); - delete repo; -} - +#if 1 cvs_sync::cvs_repository *cvs_sync::prepare_sync(const std::string &_repository, const std::string &_module, std::string const& _branch, mtncvs_state &app) { app.open(); @@ -1342,18 +448,9 @@ cvs_sync::cvs_repository *cvs_sync::prep } return repo; } +#endif -void cvs_sync::pull(const std::string &_repository, const std::string &_module, - std::string const& _branch, mtncvs_state &app) -{ cvs_repository *repo=prepare_sync(_repository,_module,_branch,app); - - // initial checkout - if (repo->empty()) - repo->prime(); - else repo->update(); - delete repo; -} - +#if 1 // used by process_sync_info cvs_file_state cvs_repository::remember(std::set &s,const file_state &fs, std::string const& filename) { for (std::set::iterator i=s.begin();i!=s.end();++i) { if (i->cvs_version==fs.cvs_version) @@ -1377,6 +474,7 @@ cvs_file_state cvs_repository::remember( I(iter.second); return iter.first; } +#endif void cvs_repository::process_sync_info(mtn_automate::sync_map_t const& sync_info, revision_id const& rid) { mtn_automate::manifest_map manifest=app.get_manifest_of(rid); @@ -1440,118 +538,6 @@ void cvs_repository::process_sync_info(m if (global_sanity.debug_p()) L(FL("%s") % debug()); } -struct cvs_repository::update_cb : cvs_client::update_callbacks -{ cvs_repository &repo; - std::vector &results; - - update_cb(cvs_repository &r, std::vector &re) - : repo(r), results(re) {} - virtual void operator()(const cvs_client::update &u) const - { results.push_back(u); - // perhaps store the file contents into the db to save storage - } -}; - -void cvs_repository::update() -{ - retrieve_modules(); - std::set::iterator now_iter=last_known_revision(); - const cvs_edge &now=*now_iter; - I(!now.revision.inner()().empty()); - std::vector file_revisions; - std::vector results; - const cvs_manifest &m=get_files(now); - file_revisions.reserve(m.size()); -#warning FIXME: changed files - for (cvs_manifest::const_iterator i=m.begin();i!=m.end();++i) - file_revisions.push_back(update_args(i->first,i->second->cvs_version, - std::string(),i->second->keyword_substitution)); - Update(file_revisions,update_cb(*this,results)); - for (std::vector::const_iterator i=results.begin();i!=results.end();++i) - { // 2do: use tags - cvs_manifest::const_iterator now_file=m.find(i->file); - std::string last_known_revision; - std::map::iterator f=files.find(i->file); - - if (now_file!=m.end()) - { last_known_revision=now_file->second->cvs_version; - I(f!=files.end()); - } - else // the file is not present in our last import - // e.g. the file is currently dead but we know an old revision - { if (f!=files.end() // we know anything about this file - && !f->second.known_states.empty()) // we have at least one known file revision - { std::set::const_iterator last=f->second.known_states.end(); - --last; - last_known_revision=last->cvs_version; - } - else f=files.insert(std::make_pair(i->file,file_history())).first; - } - if (last_known_revision=="1.1.1.1") - last_known_revision="1.1"; - std::set::const_iterator last=f->second.known_states.end(); - if (last!=f->second.known_states.begin()) --last; - - if (last_known_revision.empty()) - Log(prime_log_cb(*this,f),i->file.c_str(),"-b","-N",(void*)0); - else - // -b causes -r to get ignored on 0.12 - Log(prime_log_cb(*this,f),i->file.c_str(),/*"-b",*/"-N", - ("-r"+last_known_revision+"::").c_str(),(void*)0); - - std::string file_contents,initial_contents; - if(last==f->second.known_states.end() || last->dead) - { last=f->second.known_states.begin(); - I(last!=f->second.known_states.end()); - std::set::iterator s2=last; - cvs_client::update c=Update(i->file,s2->cvs_version); - store_checkout(s2,c,file_contents); - } - else - { I(!last->sha1sum.inner()().empty()); - file_contents=app.get_file(last->sha1sum).inner()(); - initial_contents=file_contents; - } - for (std::set::const_iterator s=last; - s!=f->second.known_states.end();++s) - { std::set::const_iterator s2=s; - ++s2; - if (s2==f->second.known_states.end()) break; - if (s2->cvs_version==i->new_revision) - { // we do not need to ask the host, we already did ... - try - { store_update(last,s2,*i,initial_contents); - } catch (informative_failure &e) - { W(F("error during update: %s\n") % e.what()); - // we _might_ try to use store delta ... - cvs_client::update c=Update(i->file,s2->cvs_version); - const_cast(s2->md5sum)=""; - const_cast(s2->patchsize)=0; - store_contents(file_data(c.contents), const_cast(s2->sha1sum)); - const_cast(s2->size)=c.contents.size(); - const_cast(s2->keyword_substitution)=c.keyword_substitution; - } - break; - } - else - { update(s,s2,i->file,file_contents); - } - } - } - drop_connection(); - - std::set::iterator dummy_iter=now_iter; - ++dummy_iter; - if (dummy_iter!=edges.end()) - { - join_edge_parts(dummy_iter); - fill_manifests(dummy_iter); - if (global_sanity.debug_p()) L(FL("%s") % debug()); - commit_cvs2mtn(dummy_iter); - } -// store_modules(); -} - static void apply_manifest_delta(cvs_manifest &base,const cvs_manifest &delta) { L(FL("apply_manifest_delta: base %d delta %d\n") % base.size() % delta.size()); for (cvs_manifest::const_iterator i=delta.begin(); i!=delta.end(); ++i) @@ -1566,40 +552,7 @@ static void apply_manifest_delta(cvs_man L(FL("apply_manifest_delta: result %d\n") % base.size()); } -const cvs_manifest &cvs_repository::get_files(const cvs_edge &e) -{ L(FL("get_files(%s %s) %s %d\n") % time_t2human(e.time) % e.revision % e.delta_base % e.xfiles.size()); #if 0 - if (!e.delta_base.inner()().empty()) - { cvs_manifest calculated_manifest; - // this is non-recursive by reason ... - const cvs_edge *current=&e; - std::vector deltas; -#if 0 // no longer delta encoded - while (!current->delta_base.inner()().empty()) - { L(FL("get_files: looking for base rev %s\n") % current->delta_base); -// ++e.cm_delta_depth; - deltas.push_back(current); - std::map::iterator>::const_iterator - cache_item=revision_lookup.find(current->delta_base); - E(cache_item!=revision_lookup.end(), - F("missing cvs cert on base revision %s\n") % current->delta_base); - current=&*(cache_item->second); - } - I(current->delta_base.inner()().empty()); -#endif - calculated_manifest=current->xfiles; - for (std::vector::const_reverse_iterator i=deltas.rbegin(); - i!=static_cast::const_reverse_iterator>(deltas.rend()); - ++i) - apply_manifest_delta(calculated_manifest,(*i)->xfiles); - e.xfiles=calculated_manifest; - e.delta_base=revision_id(); - } -#endif - return e.xfiles; -} - -#if 0 void cvs_sync::debug(const std::string &command, const std::string &arg, mtncvs_state &app) { @@ -1634,23 +587,6 @@ void cvs_sync::debug(const std::string & } #endif -const cvs_manifest &cvs_repository::get_files(const revision_id &rid) -{ std::map::iterator>::const_iterator - cache_item=revision_lookup.find(rid); - I(cache_item!=revision_lookup.end()); - return get_files(*(cache_item->second)); -} - -struct cvs_client::checkout cvs_repository::CheckOut2(const std::string &file, const std::string &revision) -{ try - { return CheckOut(file,revision); - } catch (oops &e) - { W(F("trying to reconnect, perhaps the server is confused\n")); - reconnect(); - return CheckOut(file,revision); - } -} - void cvs_client::validate_path(const std::string &local, const std::string &server) { for (std::map::const_iterator i=server_dir.begin(); i!=server_dir.end();++i) @@ -1662,172 +598,6 @@ void cvs_client::validate_path(const std server_dir[local]=server; } -static void read_file(std::string const& name, file_data &result) -{ std::string dest; - ifstream is(name.c_str()); - while (is.good()) - { char buf[10240]; - is.read(buf,sizeof buf); - if (is.gcount()) dest+=std::string(buf,buf+is.gcount()); - } - result=file_data(dest); -} - -void cvs_repository::takeover_dir(const std::string &path) -{ // remember the server path for this subdirectory - MM(path); - { std::string repository; - std::ifstream cvs_repository((path+"CVS/Repository").c_str()); - N(cvs_repository.good(), F("can't open %sCVS/Repository\n") % path); - std::getline(cvs_repository,repository); - I(!repository.empty()); - if (repository[0]!='/') repository=root+"/"+repository; - validate_path(path,repository+"/"); - } - std::ifstream cvs_Entries((path+"CVS/Entries").c_str()); - N(cvs_Entries.good(), - F("can't open %s\n") % (path+"CVS/Entries")); - L(FL("takeover_dir %s\n") % path); - static hexenc empty_file; - while (true) - { std::string line; - std::getline(cvs_Entries,line); - if (!cvs_Entries.good()) break; - if (!line.empty()) - { std::vector parts; - MM(line); - stringtok(parts,line,"/"); - // empty last part will not get created - if (parts.size()==5) parts.push_back(std::string()); - if (parts.size()!=6) - { W(F("entry line with %d components '%s'\n") % parts.size() %line); - continue; - } - if (parts[0]=="D") - { takeover_dir(path+parts[1]+"/"); - } - else // file - { // remember permissions, store file contents - I(parts[0].empty()); - std::string filename=path+parts[1]; - I(!access(filename.c_str(),R_OK)); - // parts[2]=version - // parts[3]=date - // parts[4]=keyword subst - // parts[5]='T'tag - time_t modtime=-1; - try - { modtime=cvs_client::Entries2time_t(parts[3]); - } - catch (informative_failure &e) {} - catch (std::exception &e) {} - - I(files.find(filename)==files.end()); - std::map::iterator f - =files.insert(std::make_pair(filename,file_history())).first; - file_state fs(modtime,parts[2]); - fs.author="unknown"; - fs.keyword_substitution=parts[4]; - { struct stat sbuf; - I(!stat(filename.c_str(), &sbuf)); - if (sbuf.st_mtime!=modtime) - { L(FL("modified %s %u %u\n") % filename % modtime % sbuf.st_mtime); - fs.log_msg="partially overwritten content from last update"; - store_contents(file_data(), fs.sha1sum); - f->second.known_states.insert(fs); - - fs.since_when=time(NULL); - fs.cvs_version=std::string(); - } - } - // import the file and check whether it is (un-)changed - fs.log_msg="initial cvs content"; - file_data new_data; - read_file(filename, new_data); - store_contents(new_data, fs.sha1sum); - f->second.known_states.insert(fs); - } - } - } -} - -void cvs_repository::takeover() -{ app.open(); - takeover_dir(""); - - bool need_second=false; - cvs_edge e1,e2; - e1.time=0; - e1.changelog="last cvs update (modified)"; - e1.changelog_valid=true; - e1.author="unknown"; - e2.time=time(NULL); - e2.changelog="cvs takeover"; - e2.changelog_valid=true; - e2.author="unknown"; - for (std::map::const_iterator i=files.begin(); - i!=files.end();++i) - { cvs_file_state first,second; - first=i->second.known_states.begin(); - I(first!=i->second.known_states.end()); - second=first; - ++second; - if (second==i->second.known_states.end()) second=first; - else if (!need_second) need_second=true; - if (e1.timesince_when) e1.time=first->since_when; - e1.xfiles[i->first]=first; - e2.xfiles[i->first]=second; - // at most two states known ! - I((++second)==i->second.known_states.end()); - } - if (!need_second) e1.changelog=e2.changelog; - edges.insert(e1); - if (need_second) - { edges.insert(e2); - } - // commit them all - commit_cvs2mtn(edges.begin()); - - app.close(); - // now we initialize the workspace - // mtn setup . - { std::vector args; - args.push_back(app.opts.mtn_binary); - if (args[0].empty()) args[0]="mtn"; - for (std::vector::const_iterator i=app.opts.mtn_options.begin();i!=app.opts.mtn_options.end();++i) - args.push_back(*i); - args.push_back("--branch"); - args.push_back(app.opts.branchname()); - args.push_back("setup"); - args.push_back("."); - I(args.size()<30); - const char *argv[30]; - unsigned i=0; - for (;i<30 && irevision.inner()().empty()); - of << "format_version \"1\"\n\n" - "new_manifest [0000000000000000000000000000000000000001]\n\n" - "old_revision [" << encode_hexenc((--edges.end())->revision.inner()()) << "]\n"; - } -// like in commit ? -// update_any_attrs(app); -// put_revision_id((--edges.end())->revision); -// maybe_update_inodeprints(app); -// store_modules(); -} - void cvs_sync::test(mtncvs_state &app) { I(!app.opts.revisions.empty()); @@ -1836,39 +606,6 @@ void cvs_sync::test(mtncvs_state &app) app.get_revision_certs(rid); } -// read in directory put into db -void cvs_sync::takeover(mtncvs_state &app, const std::string &_module) -{ std::string root,module=_module,branch; - - N(access("_MTN",F_OK),F("Found a _MTN file or directory, already under monotone's control?")); - { fstream cvs_root("CVS/Root"); - N(cvs_root.good(), - F("can't open ./CVS/Root, please change into the working directory\n")); - std::getline(cvs_root,root); - } - { fstream cvs_branch("CVS/Tag"); - if (cvs_branch.good()) - { std::getline(cvs_branch,branch); - MM(branch); - I(!branch.empty()); - I(branch[0]=='T'); - branch.erase(0,1); - } - } - if (module.empty()) - { fstream cvs_repository("CVS/Repository"); - N(cvs_repository.good(), - F("can't open ./CVS/Repository\n")); - std::getline(cvs_repository,module); - W(F("Guessing '%s' as the module name\n") % module); - } -// test_key_availability(app); - cvs_sync::cvs_repository repo(app,root,module,branch,false); - // FIXME? check that directory layout matches the module structure - repo.takeover(); -} -//#endif - void cvs_repository::parse_module_paths(mtn_automate::sync_map_t const& mp) { std::map sd; ============================================================ --- mtn_cvs/cvs_sync.hh 1fca0a84ef4b6803903751bfbdfc56ea5f281650 +++ mtn_cvs/cvs_sync.hh 691da87c1325aa135a3763b60b3e16a0c3a7e99a @@ -218,4 +218,8 @@ void test(mtncvs_state &app); void debug(const std::string &command, const std::string &arg, mtncvs_state &app); void takeover(mtncvs_state &app, const std::string &module); void test(mtncvs_state &app); + +// internal use +cvs_sync::cvs_repository *prepare_sync(const std::string &_repository, const std::string &_module, + std::string const& _branch, mtncvs_state &app); } // end namespace cvs_sync