#
# 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__