# # patch "ChangeLog" # from [ff8c5b74caec143b53b946742bcf9738c2c840c2] # to [b94614caa19ed2c1aae1cfcddd0fc2507556acdf] # # patch "rcs_import.cc" # from [8373f2d2ce0e3ea6e5ac087666ebca573c49d2e6] # to [5939848d4ed3b95d701024c3b99cebfa648b89fb] # # patch "tests/t_cvsimport_deleted_invar.at" # from [3c166c4e55776d45437b807b165fed231d7dcaca] # to [32da8b246eea20fe065e860c6b2428ffce7230ed] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,9 @@ +2005-05-15 graydon hoare + + * rcs_import.cc: rewrite most of the branch logic to + address issues raised in bugs 13032 and 13063. + * tests/t_cvsimport_deleted_invar.at: un-XFAIL. + 2005-05-14 Timothy Brownawell * contrib/monoprof.sh: Clean up variable definitions some. @@ -4286,7 +4292,7 @@ * AUTHORS: Mention Wojciech and Neil. * revision.cc (calculate_ancestors_from_graph): Make non-recursive. -2005-01-17 Wojciech Miłkowski +2005-01-17 Wojciech Miłkowski * std_hooks.lua: Teach about meld. --- rcs_import.cc +++ rcs_import.cc @@ -186,21 +186,10 @@ cvs_state { set in_edges; - map< cvs_key, shared_ptr > substates; }; -struct -branch_point -{ - branch_point() {} - branch_point(cvs_key const & k, - shared_ptr const & s, - size_t l) - : key(k), state(s), rev_comp_len(l) {} - cvs_key key; - shared_ptr state; - size_t rev_comp_len; -}; +typedef map > +cvs_branch; struct cvs_history @@ -220,11 +209,23 @@ cvs_key & key, shared_ptr & state); - typedef stack< shared_ptr > state_stack; - map branchpoints; + // assume admin has foo:X.Y.0.N in it, then + // this multimap contains entries of the form + // X.Y -> foo + multimap branchpoints; + + // and this map contains entries of the form + // X.Y.N.1 -> foo + map branch_first_entries; - state_stack stk; + // branch name -> branch + map > branches; + + // stack of branches we're injecting states into + stack< shared_ptr > stk; + stack< cvs_branchname > bstk; + file_path curr_file; string base_branch; @@ -235,18 +236,17 @@ cvs_history(); void set_filename(string const & file, file_id const & ident); - void push_branch(rcs_file const & r, - string const & branchpoint_version, - string const & first_branch_version); + + void index_branchpoint_symbols(rcs_file const & r); + + void push_branch(string const & branch_name, bool private_branch); + void note_file_edge(rcs_file const & r, string const & prev_rcs_version_num, string const & next_rcs_version_num, file_id const & prev_version, file_id const & next_version); - void find_branchpoint(rcs_file const & r, - string const & branchpoint_version, - string const & first_branch_version, - shared_ptr & bp_state); + void pop_branch(); }; @@ -476,7 +476,6 @@ rcs_put_raw_file_edge(next_id, curr_id, del, db); } - static void process_branch(string const & begin_version, vector< piece > const & begin_lines, @@ -510,7 +509,7 @@ insert_into_db(curr_data, curr_id, *next_lines, next_data, next_id, db); - cvs.note_file_edge (r, curr_version, next_version, + cvs.note_file_edge (r, curr_version, next_version, file_id(curr_id), file_id(next_id)); } else @@ -533,29 +532,117 @@ } } - // recursively follow any branches rooted here + + /* + + please read this exhaustingly long comment and understand it + before mucking with the branch inference logic. + + we are processing a file version. a branch might begin here. if + the current version is X.Y, then there is a branch B starting + here iff there is a symbol in the admin section called X.Y.0.Z, + where Z is the branch number (or if there is a private branch + called X.Y.Z, which is either an import branch or some private + RCS cruft). + + the version X.Y is then considered the branchpoint of B in the + current file. this does *not* mean that the CVS key -- an + abstraction representing whole-tree operations -- of X.Y is the + branchpoint across the CVS archive we're processing. + + in fact, CVS does not record the occurrence of a branching + action (tag -b). we have no idea who executed that command and + when. what we know instead is the commit X.Y immediately + preceeding the branch -- CVS consideres this the branchpoint -- + in this file's reduced view of history. we also know the first + commit X.Y.Z.1 inside the branch (which might not exist). + + our old strategy was to consider all branches nested in a + hierarchy, which was a super-tree of all the branch trees in all + the CVS files in a repository. this involved considering X.Y as + the parent version of branch X.Y.Z, an selecting "the" + branchpoint connecting the two as the least CVS key X.Y.Z.1 + committed inside the branch B. + + this was a mistake, for two significant reasons. + + first, some files do not *have* any commit inside the branch B, + only a branchpoint X.Y.0.Z. this branchpoint is actually the + last commit *before* the user branched, and could be a very old + commit, long before the branch was formed, so it is useless in + determining the branch structure. + + second, some files do not have a branch B, or worse, have + branched into B from an "ancestor" branch A, where a different + file branches into B from a different ancestor branch C. in + other words, while there *is* a tree structure within the X.Y.Z + branches of each file, there is *no* shared tree structure + between the branch names across a repository. in one file A can + be an ancestor of B, in another file B can be an ancestor of A. + + thus, we give up on establishing a hierarchy between branches + altogether. all branches exist in a flat namespace, and all are + direct descendents of the empty revision at the root of + history. each branchpoint symbol mentionned in the + administrative section of a file is considered the root of a new + lineage. + + */ + + typedef multimap::const_iterator ity; + pair range = cvs.branchpoints.equal_range(curr_version); + if (range.first != cvs.branchpoints.end() + && range.first->first == curr_version) + { + // here we are making a slightly odd note that the file + // springs into existence on the branch, with an edge from + // the null id to the branchpoint. we do this because we are + // transplanting the RCS file branchpoint (which is + // hierarchical) onto a tree-wide branch root (which is + // non-hierarchical, starting from the null revision). + + for (ity branch = range.first; branch != range.second; ++branch) + { + L(F("noting branchpoint for %s = %s\n") % branch->first % curr_version); + cvs.push_branch(branch->second, false); + cvs.note_file_edge (r, curr_version, curr_version, + file_id(), file_id(curr_id)); + cvs.pop_branch(); + } + } + + // recursively follow any branch commits coming from the branchpoint boost::shared_ptr curr_delta = r.deltas.find(curr_version)->second; for(vector::const_iterator i = curr_delta->branches.begin(); - i != curr_delta->branches.end(); ++i) - { - L(F("following RCS branch %s\n") % (*i)); - vector< piece > branch_lines; - construct_version(*curr_lines, *i, branch_lines, r); - - data branch_data; - hexenc branch_id; - insert_into_db(curr_data, curr_id, - branch_lines, branch_data, branch_id, db); - cvs.push_branch (r, curr_version, *i); + i != curr_delta->branches.end(); ++i) + { + string branch; + bool priv = false; + map::const_iterator be = cvs.branch_first_entries.find(*i); - cvs.note_file_edge (r, curr_version, *i, - file_id(curr_id), file_id(branch_id)); + if (be != cvs.branch_first_entries.end()) + branch = be->second; + else + priv = true; + + L(F("following RCS branch %s = '%s'\n") % (*i) % branch); + vector< piece > branch_lines; + construct_version(*curr_lines, *i, branch_lines, r); + + data branch_data; + hexenc branch_id; + insert_into_db(curr_data, curr_id, + branch_lines, branch_data, branch_id, db); + + cvs.push_branch (branch, priv); + cvs.note_file_edge (r, curr_version, *i, + file_id(curr_id), file_id(branch_id)); + process_branch(*i, branch_lines, branch_data, branch_id, r, db, cvs); + cvs.pop_branch(); + + L(F("finished RCS branch %s = '%s'\n") % (*i) % branch); + } - process_branch(*i, branch_lines, branch_data, branch_id, r, db, cvs); - cvs.pop_branch(); - L(F("finished RCS branch %s\n") % (*i)); - } - if (!r.deltas.find(curr_version)->second->next.empty()) { // advance curr_data = next_data; @@ -588,6 +675,7 @@ file_id fid = id; cvs.set_filename (filename, fid); + cvs.index_branchpoint_symbols (r); if (! db.file_version_exists (fid)) { @@ -675,70 +763,26 @@ } static void -version_to_components(string const & version, - vector & components) +split_version(string const & v, vector & vs) { - typedef boost::tokenizer > tokenizer; + vs.clear(); boost::char_separator sep("."); - tokenizer tokens(version, sep); - - components.clear(); - copy(tokens.begin(), tokens.end(), back_inserter(components)); + typedef boost::tokenizer > tokenizer; + tokenizer tokens(v, sep); + copy(tokens.begin(), tokens.end(), back_inserter(vs)); } -static string -find_branch_for_version(multimap const & symbols, - string const & version, - string const & base) +static void +join_version(vector const & vs, string & v) { - typedef multimap::const_iterator ity; - - L(F("looking up branch name for %s\n") % version); - - vector components; - version_to_components(version, components); - - if (components.size() < 4) + v.clear(); + for (vector::const_iterator i = vs.begin(); + i != vs.end(); ++i) { - L(F("version %s has too few components, using branch %s\n") - % version % base); - return base; + if (i != vs.begin()) + v += "."; + v += *i; } - - string branch_version; - components[components.size() - 1] = components[components.size() - 2]; - components[components.size() - 2] = "0"; - for (size_t i = 0; i < components.size(); ++i) - { - if (i != 0) - branch_version += "."; - branch_version += components[i]; - } - - pair range = symbols.equal_range(branch_version); - if (range.first == symbols.end()) - { - L(F("no branch %s found, using base '%s'\n") - % branch_version % base); - return base; - } - else - { - string res = base; - res += "."; - res += range.first->second; - int num_results = 0; - while (range.first != range.second) - { range.first++; num_results++; } - - if (num_results > 1) - W(F("multiple entries (%d) for branch %s found, using: '%s'\n") - % num_results % branch_version % res); - else - L(F("unique entry for branch %s found: '%s'\n") - % branch_version % res); - return res; - } } cvs_key::cvs_key(rcs_file const & r, string const & version, @@ -777,10 +821,7 @@ id = nextid++; } - string branch_name = find_branch_for_version(r.admin.symbols, - version, - cvs.base_branch); - branch = cvs.branch_interner.intern(branch_name); + branch = cvs.bstk.top(); changelog = cvs.changelog_interner.intern(deltatext->second->log); author = cvs.author_interner.intern(delta->second->author); } @@ -790,7 +831,6 @@ n_versions("versions", "v", 1), n_tree_branches("branches", "b", 1) { - stk.push(shared_ptr(new cvs_state())); } void @@ -811,6 +851,42 @@ curr_file = file_path(ss); } +void cvs_history::index_branchpoint_symbols(rcs_file const & r) +{ + branchpoints.clear(); + branch_first_entries.clear(); + + for (std::multimap::const_iterator i = + r.admin.symbols.begin(); i != r.admin.symbols.end(); ++i) + { + std::string const & num = i->first; + std::string const & sym = i->second; + + vector components; + split_version(num, components); + + if (components.size() > 2 && + components[components.size() - 2] == string("0")) + { + string first_entry_version; + components[components.size() - 2] = components[components.size() - 1]; + components[components.size() - 1] = string("1"); + join_version(components, first_entry_version); + + L(F("first version in branch %s would be %s\n") + % sym % first_entry_version); + branch_first_entries.insert(make_pair(first_entry_version, sym)); + + string branchpoint_version; + components.erase(components.end() - 2, components.end()); + join_version(components, branchpoint_version); + + L(F("file branchpoint for %s at %s\n") % sym % branchpoint_version); + branchpoints.insert(make_pair(branchpoint_version, sym)); + } + } +} + bool cvs_history::find_key_and_state(rcs_file const & r, string const & version, @@ -818,7 +894,7 @@ shared_ptr & state) { I(stk.size() > 0); - map< cvs_key, shared_ptr > & substates = stk.top()->substates; + shared_ptr branch = stk.top(); cvs_key nk(r, version, *this); nk.add_file(curr_file, version); @@ -839,8 +915,8 @@ if (static_cast(k_old.time - constants::cvs_window / 2) < k_old.time) k_old.time -= constants::cvs_window / 2; - i_new = substates.lower_bound(k_new); - i_old = substates.upper_bound(k_old); + i_new = branch->lower_bound(k_new); + i_old = branch->upper_bound(k_old); for (i = i_new; i != i_old; ++i) { @@ -849,99 +925,38 @@ key = i->first; state = i->second; key.add_file(curr_file, version); - substates.erase(i->first); - substates.insert(make_pair(key, state)); return true; } } key = nk; state = shared_ptr(new cvs_state()); - substates.insert(make_pair(key, state)); + branch->insert(make_pair(key, state)); return false; } -void -cvs_history::find_branchpoint(rcs_file const & r, - string const & branchpoint_version, - string const & first_branch_version, - shared_ptr & bp_state) -{ - cvs_key k; - I(find_key_and_state(r, branchpoint_version, k, bp_state)); +void +cvs_history::push_branch(string const & branch_name, bool private_branch) +{ + shared_ptr branch; - string branch_name = find_branch_for_version(r.admin.symbols, - first_branch_version, - base_branch); + I(stk.size() > 0); - unsigned long branch = branch_interner.intern(branch_name); - - map::const_iterator i - = branchpoints.find(branch); - - vector new_components; - version_to_components(branchpoint_version, new_components); - size_t newlen = new_components.size(); - - - if (i == branchpoints.end()) + map >::const_iterator b = branches.find(branch_name); + if (b == branches.end()) { - ++n_tree_branches; - L(F("beginning branch %s at %s : %s\n") - % branch_name % curr_file % branchpoint_version); - branchpoints.insert(make_pair(branch, - branch_point(k, bp_state, newlen))); + branch = shared_ptr(new cvs_branch()); + if (!private_branch) + branches.insert(make_pair(branch_name, branch)); } else - { - // if this version comes off the main trunk, but the previous branchpoint - // version comes off a branch (such as an import 1.1.1.1), then we want - // to take the one closest to the trunk. - // TODO perhaps we need to reconsider branching in general for cvs_import, - // this is pretty much just a workaround for 1.1<->1.1.1.1 equivalence + branch = b->second; - // take the earlier of the new key and the existing branchpoint - if ((k.time < i->second.key.time && newlen <= i->second.rev_comp_len) - || newlen < i->second.rev_comp_len) - { - L(F("moving branch %s back to %s : %s\n") - % branch_name % curr_file % branchpoint_version); - shared_ptr old = i->second.state; - set moved; - for (map< cvs_key, shared_ptr >::const_iterator j = - old->substates.begin(); j != old->substates.end(); ++j) - { - if (j->first.branch == branch) - { - bp_state->substates.insert(*j); - moved.insert(j->first); - } - } - for (set::const_iterator j = moved.begin(); j != moved.end(); - ++j) - { - old->substates.erase(*j); - } - branchpoints[branch] = branch_point(k, bp_state, newlen); - } - else - { - L(F("using existing branchpoint for %s at %s : %s\n") - % branch_name % curr_file % branchpoint_version); - bp_state = i->second.state; - } - } -} + stk.push(branch); -void -cvs_history::push_branch(rcs_file const & r, - string const & branchpoint_version, - string const & first_branch_version) -{ - shared_ptr bp_state; - I(stk.size() > 0); - find_branchpoint(r, branchpoint_version, - first_branch_version, bp_state); - stk.push(bp_state); + if (private_branch) + bstk.push(bstk.top()); + else + bstk.push(branch_interner.intern(base_branch + "." + branch_name)); } void @@ -957,7 +972,9 @@ I(stk.size() > 0); I(! curr_file().empty()); - + + L(F("noting file edge %s -> %s\n") % prev_rcs_version_num % next_rcs_version_num); + // we can't use operator[] since it is non-const std::map >::const_iterator prev_delta = r.deltas.find(prev_rcs_version_num), @@ -1004,8 +1021,8 @@ cvs_history::pop_branch() { I(stk.size() > 1); - I(stk.top()->substates.size() > 0); stk.pop(); + bstk.pop(); } @@ -1041,21 +1058,17 @@ manifest_id const & child_mid, app_state & app, cvs_history & cvs, - unsigned long depth, bool head_manifest_p) { - if (depth == 0) - L(F("storing trunk manifest %s (base %s)\n") % parent_mid % child_mid); - else - L(F("storing branch manifest %s (base %s)\n") % child_mid % parent_mid); + L(F("storing manifest %s (base %s)\n") % parent_mid % child_mid); - if (depth == 0 && head_manifest_p) + if (head_manifest_p) { - L(F("storing trunk head %s\n") % child_mid); - // the trunk branch has one very important manifest: the head. - // this is the "newest" of all manifests within the import, and - // we store it in its entirety. + L(F("storing head %s\n") % child_mid); + // a branch has one very important manifest: the head. this is + // the "newest" of all manifests within the branch (including + // the trunk), and we store it in its entirety. if (! app.db.manifest_version_exists(child_mid)) { manifest_data mdat; @@ -1070,81 +1083,44 @@ return; } - unsigned long p, c, older, newer; - p = cvs.manifest_version_interner.intern(parent_mid.inner()()); - c = cvs.manifest_version_interner.intern(child_mid.inner()()); - older = (depth == 0) ? p : c; - newer = (depth == 0) ? c : p; + unsigned long older, newer; + + older = cvs.manifest_version_interner.intern(parent_mid.inner()()); + newer = cvs.manifest_version_interner.intern(child_mid.inner()()); + if (cvs.manifest_cycle_detector.edge_makes_cycle(older,newer)) { - if (depth == 0) - { - L(F("skipping cyclical trunk manifest delta %s -> %s\n") - % parent_mid % child_mid); - // if this is on the trunk, we are potentially breaking the chain - // one would use to get to p. we need to make sure p exists. - if (!app.db.manifest_version_exists(parent_mid)) - { - L(F("writing full manifest %s\n") % parent_mid); - manifest_data mdat; - write_manifest_map(parent, mdat); - app.db.put_manifest(parent_mid, mdat); - } - } - else - { - L(F("skipping cyclical branch manifest delta %s -> %s\n") - % child_mid % parent_mid); - // if this is on a branch, we are potentially breaking the chain one - // would use to get to c. we need to make sure c exists. - if (!app.db.manifest_version_exists(child_mid)) - { - L(F("writing full manifest %s\n") % child_mid); - manifest_data mdat; - write_manifest_map(child, mdat); - app.db.put_manifest(child_mid, mdat); - } - } + + L(F("skipping cyclical manifest delta %s -> %s\n") + % parent_mid % child_mid); + // we are potentially breaking the chain one would use to get to + // p. we need to make sure p exists. + if (!app.db.manifest_version_exists(parent_mid)) + { + L(F("writing full manifest %s\n") % parent_mid); + manifest_data mdat; + write_manifest_map(parent, mdat); + app.db.put_manifest(parent_mid, mdat); + } return; } cvs.manifest_cycle_detector.put_edge(older,newer); - if (depth == 0) - { - L(F("storing trunk manifest delta %s -> %s\n") - % child_mid % parent_mid); + + L(F("storing manifest delta %s -> %s\n") + % child_mid % parent_mid); + + // the ancestry-based 'child' is a 'new' version as far as the + // storage system is concerned; that is to say that the + // ancestry-based 'parent' is a temporally older tree version, which + // can be constructed from the 'newer' child. so the delta should + // run from child (new) -> parent (old). - // in this case, the ancestry-based 'child' is on a trunk, so it is - // a 'new' version as far as the storage system is concerned; that - // is to say that the ancestry-based 'parent' is a temporally older - // tree version, which can be constructed from the 'newer' child. so - // the delta should run from child (new) -> parent (old). - - delta del; - diff(child, parent, del); - rcs_put_raw_manifest_edge(parent_mid.inner(), - child_mid.inner(), - del, app.db); - } - else - { - L(F("storing branch manifest delta %s -> %s\n") - % parent_mid % child_mid); - - // in this case, the ancestry-based 'child' is on a branch, so it is - // an 'old' version as far as the storage system is concerned; that - // is to say it is constructed by first building a 'new' version (the - // ancestry-based 'parent') and then following a delta to the - // child. remember that the storage system assumes that all deltas go - // from temporally new -> temporally old. so the delta should go from - // parent (new) -> child (old) - - delta del; - diff(parent, child, del); - rcs_put_raw_manifest_edge(child_mid.inner(), - parent_mid.inner(), - del, app.db); - } + delta del; + diff(child, parent, del); + rcs_put_raw_manifest_edge(parent_mid.inner(), + child_mid.inner(), + del, app.db); } @@ -1217,89 +1193,23 @@ % state->in_edges.size()); } - static void -import_states_recursive(ticker & n_edges, - ticker & n_branches, - shared_ptr state, - cvs_branchname branch_filter, - revision_id parent_rid, - manifest_id parent_mid, - manifest_map parent_map, - cvs_history & cvs, - app_state & app, - vector< pair > & revisions, - unsigned long depth); - -static void -import_states_by_branch(ticker & n_edges, - ticker & n_branches, - shared_ptr state, - revision_id const & parent_rid, - manifest_id const & parent_mid, - manifest_map const & parent_map, - cvs_history & cvs, - app_state & app, - vector< pair > & revisions, - unsigned long depth) +import_branch_states(ticker & n_edges, + cvs_branch & branch, + cvs_history & cvs, + app_state & app, + vector< pair > & revisions) { - set branches; - - // collect all the branches - for (map< cvs_key, shared_ptr >::reverse_iterator i = state->substates.rbegin(); - i != state->substates.rend(); ++i) - branches.insert(i->first.branch); - - // walk each sub-branch in order - for (set::const_iterator branch = branches.begin(); - branch != branches.end(); ++branch) - { - import_states_recursive(n_edges, n_branches, state, *branch, - parent_rid, parent_mid, parent_map, - cvs, app, revisions, depth); - } -} - -static void -import_states_recursive(ticker & n_edges, - ticker & n_branches, - shared_ptr state, - cvs_branchname branch_filter, - revision_id parent_rid, - manifest_id parent_mid, - manifest_map parent_map, - cvs_history & cvs, - app_state & app, - vector< pair > & revisions, - unsigned long depth) -{ - if (state->substates.size() > 0) - ++n_branches; - - manifest_id child_mid; - revision_id child_rid; - manifest_map child_map = parent_map; + manifest_map parent_map, child_map; + manifest_id parent_mid, child_mid; + revision_id parent_rid, child_rid; - string branchname = cvs.branch_interner.lookup(branch_filter); - ui.set_tick_trailer("building branch " + branchname); + // we look through the branch temporally *backwards* from oldest to + // newest - // these are all sub-branches, so we look through them temporally - // *backwards* from oldest to newest - map< cvs_key, shared_ptr >::reverse_iterator newest_branch_state; - for (map< cvs_key, shared_ptr >::reverse_iterator i = state->substates.rbegin(); - i != state->substates.rend(); ++i) + for (cvs_branch::reverse_iterator i = branch.rbegin(); + i != branch.rend(); ++i) { - if (i->first.branch != branch_filter) - continue; - newest_branch_state = i; - } - - for (map< cvs_key, shared_ptr >::reverse_iterator i = state->substates.rbegin(); - i != state->substates.rend(); ++i) - { - if (i->first.branch != branch_filter) - continue; - revision_set rev; boost::shared_ptr cs(new change_set()); build_change_set(i->second, parent_map, cvs, *cs); @@ -1315,13 +1225,8 @@ store_manifest_edge(parent_map, child_map, parent_mid, child_mid, - app, cvs, depth, i == newest_branch_state); + app, cvs, i->first == branch.begin()->first); - if (i->second->substates.size() > 0) - import_states_by_branch(n_edges, n_branches, i->second, - child_rid, child_mid, child_map, - cvs, app, revisions, depth+1); - // now apply same change set to parent_map, making parent_map == child_map apply_change_set(*cs, parent_map); parent_mid = child_mid; @@ -1351,6 +1256,10 @@ N(app.branch_name() != "", F("need base --branch argument for importing")); cvs.base_branch = app.branch_name(); + // push the trunk + cvs.stk.push(shared_ptr(new cvs_branch())); + cvs.bstk.push(cvs.branch_interner.intern(cvs.base_branch)); + { transaction_guard guard(app.db); cvs_tree_walker walker(cvs, app.db); @@ -1368,7 +1277,6 @@ P(F("phase 1 (version import) complete\n")); I(cvs.stk.size() == 1); - shared_ptr state = cvs.stk.top(); vector< pair > revisions; { @@ -1378,10 +1286,19 @@ manifest_map root_manifest; manifest_id root_mid; revision_id root_rid; + - import_states_by_branch(n_edges, n_branches, state, - root_rid, root_mid, - root_manifest, cvs, app, revisions, 0); + ui.set_tick_trailer("building trunk"); + import_branch_states(n_edges, *cvs.stk.top(), cvs, app, revisions); + + for(map >::const_iterator branch = cvs.branches.begin(); + branch != cvs.branches.end(); ++branch) + { + ui.set_tick_trailer("building branch " + branch->first); + ++n_branches; + import_branch_states(n_edges, *(branch->second), cvs, app, revisions); + } + P(F("phase 2 (ancestry reconstruction) complete\n")); guard.commit(); } --- tests/t_cvsimport_deleted_invar.at +++ tests/t_cvsimport_deleted_invar.at @@ -1,8 +1,7 @@ # -*- Autoconf -*- AT_SETUP([cvs import, deleted file invariant]) AT_KEYWORDS(cvs) -AT_XFAIL_IF(true) NEED_UNGZB64 MONOTONE_SETUP