# # patch "ChangeLog" # from [34847c5d937ac67773f07cab8b9d83cd45ab627c] # to [f7385f3baf0dfdb837ac59c0eb9f78b60a0615af] # # patch "commands.cc" # from [0219fa13f31351d209832ea210bb1e7a46bef761] # to [f42dd73b103bd0114e827b41ac89b0d75bbb07f4] # # patch "rcs_file.cc" # from [888ba309a3a161cddb6f63f510e1e9bfb7b7da63] # to [8f2bc1920dfa4a94fd265b03e081da13b3a171a8] # # patch "rcs_import.cc" # from [0b7f304fb69c215b7555bca255db16bc89106489] # to [aed7c1a1c6a7bb6ca04a7676f3225b80b664e8e3] # # patch "rcs_import.hh" # from [6b3bc7d569e1f4fb65238ad258c11793c9dbca82] # to [eba99da6a1751f001de5a9937f8f71852836553b] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,21 @@ +2005-06-22 graydon hoare + + * rcs_file.cc: Track file:line numbers, accept files which violate + some lies in rcs file format. + * rcs_import.cc (cvs_tree_walker): + Warn rather than crash on parse errors. + (cvs_history) + (cvs_commit) + (cvs_cluster) + (prepared_revision) + (import_branch) + (import_cvs_repo): Support non-branch tags. + +2005-06-21 graydon hoare + + * rcs_import.{cc,hh} (import_rcs_file): Rename to test_parse_rcs_file. + * commands.cc (rcs_import): rename call. + 2005-06-19 graydon hoare * rcs_import.cc: Rewrite change set inference logic. --- commands.cc +++ commands.cc @@ -3473,20 +3473,18 @@ CMD(rcs_import, "debug", "RCSFILE...", - "import all versions in RCS files\n" - "this command doesn't reconstruct revisions. you probably want cvs_import", + "parse versions in RCS files\n" + "this command doesn't reconstruct or import revisions. you probably want cvs_import", OPT_BRANCH_NAME) { if (args.size() < 1) throw usage(name); - transaction_guard guard(app.db); for (vector::const_iterator i = args.begin(); i != args.end(); ++i) { - import_rcs_file(mkpath((*i)()), app.db); + test_parse_rcs_file(mkpath((*i)()), app.db); } - guard.commit(); } --- rcs_file.cc +++ rcs_file.cc @@ -196,9 +196,23 @@ } token_type; +static inline void +adv(char i, size_t & line, size_t & col) +{ + if (i == '\n') + { + col = 0; + ++line; + } + else + ++col; +} + static token_type get_token(file_source & ist, - std::string & str) + std::string & str, + size_t & line, + size_t & col) { bool saw_idchar = false; int i = ist.peek(); @@ -210,6 +224,7 @@ { if (i == EOF) return TOK_NONE; + adv(i, line, col); if (!isspace(i)) break; ist.get(c); @@ -220,27 +235,33 @@ { case ';': ist.get(c); + ++col; return TOK_SEMI; break; case ':': ist.get(c); + ++col; return TOK_COLON; break; case '@': ist.get(c); + ++col; while (ist.get(c)) { if (c == '@') { if (ist.peek() == '@') - { ist.get(c); str += c; } + { ist.get(c); str += c; ++col; } else break; } else - str += c; + { + adv(i, line, col); + str += c; + } } return TOK_STRING; break; @@ -252,6 +273,7 @@ && !isspace(i)) { ist.get(c); + ++col; if (! isdigit(c) && c != '.') saw_idchar = true; str += c; @@ -275,9 +297,11 @@ std::string token; token_type ttype; + size_t line, col; + parser(file_source & s, rcs_file & r) - : ist(s), r(r) + : ist(s), r(r), line(1), col(1) {} std::string tt2str(token_type tt) @@ -302,7 +326,7 @@ void advance() { - ttype = get_token(ist, token); + ttype = get_token(ist, token, line, col); // std::cerr << tt2str(ttype) << ": " << token << std::endl; } @@ -316,12 +340,8 @@ void eat(token_type want) { if (ttype != want) - throw oops("parse failure: expecting " - + tt2str(want) - + " got " - + tt2str(ttype) - + " with value: " - + token); + throw oops((F("parse failure %d:%d: expecting %s, got %s with value '%s'\n") + % line % col % tt2str(want) % tt2str(ttype) % token).str()); advance(); } @@ -339,10 +359,8 @@ { std::string tmp; if (!symp(expected)) - throw oops(std::string("parse failure: ") - + "expecting word '" - + expected - + "'"); + throw oops((F("parse failure %d:%d: expecting word '%s'\n") + % line % col % expected).str()); advance(); } @@ -356,7 +374,8 @@ void word() { if (!wordp()) - throw oops("expecting word"); + throw oops((F("parse failure %d:%d: expecting word\n") + % line % col).str()); advance(); } @@ -376,10 +395,23 @@ if (symp("branch")) { sym(r.admin.branch); if (nump()) num(); semi(); } expect("access"); while(symp()) { sym(); } semi(); expect("symbols"); - while(symp()) + + // "man rcsfile" lies: there are real files in the wild which use + // num tokens as the key value in a symbols entry. for example + // "3.1:1.1.0.2" is a real sym:num specification, despite "3.1" + // being a num itself, not a sym. + + while(symp() || nump()) { std::string stmp, ntmp; - sym(stmp); colon(); num(ntmp); + if (symp()) + { + sym(stmp); colon(); num(ntmp); + } + else + { + num(stmp); colon(); num(ntmp); + } r.admin.symbols.insert(make_pair(ntmp, stmp)); } semi(); --- rcs_import.cc +++ rcs_import.cc @@ -62,18 +62,6 @@ struct cvs_commit { - cvs_commit(time_t t, - cvs_author a, - cvs_changelog c, - cvs_version v, - cvs_path p) - : time(t), - author(a), - changelog(c), - version(v), - path(p) - {} - cvs_commit(rcs_file const & r, string const & rcs_version, file_id const & ident, @@ -85,7 +73,8 @@ cvs_changelog changelog; cvs_version version; cvs_path path; - + vector tags; + bool operator<(cvs_commit const & other) const { return time < other.time; @@ -101,7 +90,7 @@ time_t first_commit; map live_at_beginning; - vector lineage; + vector lineage; void note_commit(time_t now) { @@ -142,6 +131,7 @@ interner changelog_interner; interner file_version_interner; interner path_interner; + interner tag_interner; interner manifest_version_interner; cycle_detector manifest_cycle_detector; @@ -163,6 +153,12 @@ stack< shared_ptr > stk; stack< cvs_branchname > bstk; + // tag -> time, revision + // + // used to resolve the *last* revision which has a given tag + // applied; this is the revision which wins the tag. + map > resolved_tags; + file_path curr_file; cvs_path curr_file_interned; @@ -226,6 +222,18 @@ author = cvs.author_interner.intern(delta->second->author); path = cvs.curr_file_interned; version = cvs.file_version_interner.intern(ident.inner()()); + + typedef multimap::const_iterator ity; + pair range = r.admin.symbols.equal_range(rcs_version); + for (ity i = range.first; i != range.second; ++i) + { + if (i->first == rcs_version) + { + L(F("version %s -> tag %s\n") % rcs_version % i->second); + tags.push_back(cvs.tag_interner.intern(i->second)); + } + } + } @@ -660,26 +668,18 @@ void -import_rcs_file(fs::path const & filename, database & db) +test_parse_rcs_file(fs::path const & filename, database & db) { cvs_history cvs; - I(! fs::is_directory(filename)); I(! filename.empty()); + I(fs::exists(filename)); + I(! fs::is_directory(filename)); - fs::path leaf = mkpath(filename.leaf()); - fs::path branch = mkpath(filename.branch_path().string()); - - I(! branch.empty()); - I(! leaf.empty()); - I( fs::is_directory(branch)); - I( fs::exists(branch)); - - I(chdir(filename.branch_path().native_directory_string().c_str()) == 0); - - I(fs::exists(leaf)); - - import_rcs_file_with_cvs(leaf.native_file_string(), db, cvs); + P(F("parsing RCS file %s\n") % filename.string()); + rcs_file r; + parse_rcs_file(filename.string(), r); + P(F("parsed RCS file %s OK\n") % filename.string()); } @@ -828,7 +828,14 @@ string file = path(); if (file.substr(file.size() - 2) == string(",v")) { - import_rcs_file_with_cvs(file, db, cvs); + try + { + import_rcs_file_with_cvs(file, db, cvs); + } + catch (oops const & o) + { + W(F("error reading RCS file %s: %s\n") % file % o.what()); + } } else L(F("skipping non-RCS file %s\n") % file); @@ -901,6 +908,7 @@ time_t first_time; cvs_author author; cvs_changelog changelog; + set tags; cvs_cluster(time_t t, cvs_author a, @@ -947,6 +955,7 @@ time_t time; cvs_author author; cvs_changelog changelog; + vector tags; }; vector preps; @@ -1072,6 +1081,11 @@ cvs_cluster::entry(i->alive, i->version, i->time))); + for (vector::const_iterator j = i->tags.begin(); + j != i->tags.end(); ++j) + { + target->tags.insert(*j); + } } @@ -1143,6 +1157,19 @@ L(F("trunk has %d entries\n") % cvs.trunk->lineage.size()); import_branch(cvs, app, cvs.base_branch, cvs.trunk, n_revs); + // now we have a "last" rev for each tag + { + packet_db_writer dbw(app); + for (map >::const_iterator i = cvs.resolved_tags.begin(); + i != cvs.resolved_tags.end(); ++i) + { + string tag = cvs.tag_interner.lookup(i->first); + ui.set_tick_trailer("marking tag " + tag); + cert_revision_tag(i->second.second, tag, app, dbw); + } + } + + return; } @@ -1197,6 +1224,11 @@ author(c.author), changelog(c.changelog) { + for (set::const_iterator i = c.tags.begin(); + i != c.tags.end(); ++i) + { + tags.push_back(*i); + } } @@ -1292,6 +1324,28 @@ cluster_consumer::store_auxiliary_certs(prepared_revision const & p) { packet_db_writer dbw(app); + + for (vector::const_iterator i = p.tags.begin(); + i != p.tags.end(); ++i) + { + map >::const_iterator j + = cvs.resolved_tags.find(*i); + + if (j != cvs.resolved_tags.end()) + { + if (j->second.first < p.time) + { + // move the tag forwards + cvs.resolved_tags.erase(*i); + cvs.resolved_tags.insert(make_pair(*i, make_pair(p.time, p.rid))); + } + } + else + { + cvs.resolved_tags.insert(make_pair(*i, make_pair(p.time, p.rid))); + } + } + cert_revision_in_branch(p.rid, cert_value(branchname), app, dbw); cert_revision_author(p.rid, cvs.author_interner.lookup(p.author), app, dbw); cert_revision_changelog(p.rid, cvs.changelog_interner.lookup(p.changelog), app, dbw); --- rcs_import.hh +++ rcs_import.hh @@ -9,7 +9,7 @@ #include "vocab.hh" #include "database.hh" -void import_rcs_file(fs::path const & filename, database & db); +void test_parse_rcs_file(fs::path const & filename, database & db); void import_cvs_repo(fs::path const & cvsroot, app_state & app); #endif // __RCS_IMPORT_HH__