# # old_revision [3bb50ab18478fb90622ec346ef2d7d391656bf7f] # # add_file "tests/t_cvsimport_branch.at" # content [ea343da64af4cd12f49af7b8acda58d7d62efaae] # # add_file "tests/t_cvsimport_branch2.at" # content [180b0e4decc553b3ab484f9d6a7cac9d607a48bb] # # patch "ChangeLog" # from [7fbfacfd6eb7f8444712762f48501ae2b206ad6b] # to [aab0cdb4b9f37d420fc9e71711fc97ccfb2f1fe4] # # patch "rcs_import.cc" # from [0de2b62e2c973f4c94592cff4990f0a43eb70294] # to [e286ee7238e5811f587dba45a3b43b71900fb704] # # patch "testsuite.at" # from [7f433b14b3bd2a67eeacc49577a64e35d97ed02f] # to [c5cc87a93f09292b84428622d48f099b96d8b8a1] # ============================================================ --- tests/t_cvsimport_branch.at ea343da64af4cd12f49af7b8acda58d7d62efaae +++ tests/t_cvsimport_branch.at ea343da64af4cd12f49af7b8acda58d7d62efaae @@ -0,0 +1,115 @@ +# -*- Autoconf -*- + +AT_SETUP([importing CVS branches with ancestory]) + +MONOTONE_SETUP + +AT_DATA(file1.0, [version 0 of test file1 +]) + +AT_DATA(file1.1, [version 1 of test file1 +]) + +AT_DATA(file1.2, [version 2 of test file1 +]) + +AT_DATA(file2.0, [version 0 of test file2 +]) + +AT_DATA(file2.1, [version 1 of test file2 +]) + +AT_DATA(changelog.0, [first changelog entry +]) + +AT_DATA(changelog.1, [second changelog + +first changelog entry +]) + +AT_DATA(changelog.2, [third changelog -not on branch- + +second changelog + +first changelog entry +]) + +AT_DATA(changelog.3, [third changelog -on branch- + +second changelog + +first changelog entry +]) + +AT_DATA(branchlist, [test +test.branched +]) + +# build the cvs repository + +CVSROOT=`pwd`/cvs-repository +AT_CHECK(cvs -q -d $CVSROOT init, [0], [ignore], [ignore]) +AT_CHECK(test -e $CVSROOT) +AT_CHECK(test -e $CVSROOT/CVSROOT) +AT_CHECK(test -e $CVSROOT/CVSROOT/modules) + +# checkout the empty repository and commit some files + +AT_CHECK(cvs -d $CVSROOT co ., [], [ignore], [ignore]) +AT_CHECK(mkdir testdir) +AT_CHECK(cp file1.0 testdir/file1) +AT_CHECK(cp file2.0 testdir/file2) +AT_CHECK(cp changelog.0 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT add testdir, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT add testdir/file1, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT add testdir/file2, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT add testdir/changelog, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT commit -m 'initial import' testdir/file1 testdir/file2 testdir/changelog, [], [ignore], [ignore]) + +# commit first changes +AT_CHECK(cp file1.1 testdir/file1) +AT_CHECK(cp changelog.1 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT commit -m 'first commit' testdir/file1 testdir/changelog, [], [ignore], [ignore]) + +# now we create a branch +AT_CHECK(cd testdir; cvs -d $CVSROOT tag -b branched, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT up -r branched, [], [ignore], [ignore]) + +# alter the files on the branch +AT_CHECK(cp file2.1 testdir/file2) +AT_CHECK(cp changelog.3 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT commit -m 'commit on branch' testdir/file2 testdir/changelog, [], [ignore], [ignore]) + +# and create some mainline changes after the branch +AT_CHECK(cvs -d $CVSROOT up -A, [], [ignore], [ignore]) +AT_CHECK(cp file1.2 testdir/file1) +AT_CHECK(cp changelog.2 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT commit -m 'commit on mainline after branch' testdir/file1 testdir/changelog, [], [ignore], [ignore]) + +# import into monotone and check presence of files +AT_CHECK(MONOTONE --branch=test cvs_import $CVSROOT/testdir, [], [ignore], [ignore]) + +# check if all branches were imported +AT_CHECK(MONOTONE list branches, [], [stdout], [ignore]) +AT_CHECK(cmp stdout branchlist) + +# checkout the imported repository into maindir and branchdir +AT_CHECK(MONOTONE checkout --branch=test maindir, [], [ignore], [ignore]) +AT_CHECK(MONOTONE checkout --branch=test.branched branchdir, [], [ignore], [ignore]) + +# check for correctness of the files in the main tree +AT_CHECK(cmp file1.2 maindir/file1) +AT_CHECK(cmp file2.0 maindir/file2) +AT_CHECK(cmp changelog.2 maindir/changelog) + +# check for correctness of the files in the branch +AT_CHECK(cmp file1.1 branchdir/file1) +AT_CHECK(cmp file2.1 branchdir/file2) +AT_CHECK(cmp changelog.3 branchdir/changelog) + +# get the log of the branch to check for correct branchpoint +AT_CHECK(cd branchdir; MONOTONE log, [], [stdout], [ignore]) +AT_CHECK(grep "commit on branch" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "initial import" stdout, [], [ignore], [ignore]) + +AT_CLEANUP ============================================================ --- tests/t_cvsimport_branch2.at 180b0e4decc553b3ab484f9d6a7cac9d607a48bb +++ tests/t_cvsimport_branch2.at 180b0e4decc553b3ab484f9d6a7cac9d607a48bb @@ -0,0 +1,230 @@ +# -*- Autoconf -*- + +AT_SETUP([importing complex CVS branches w/ ancestory]) + +# +# Branch layout overview: +# +# Root +# | +# A +# |\ +# | C -- D +# | +# B +# +# Root contains file1, file2 and changelog, branch C adds a file3, but does +# touch any other file. D then updates all files. No commits on branch B, thus +# branch B is not imported. +# +# + +MONOTONE_SETUP + +AT_DATA(file1.0, [version 0 of test file1 +]) + +AT_DATA(file1.1, [version 1 of test file1 +]) + +AT_DATA(file1.2, [version 2 of test file1 +]) + +AT_DATA(file1.3, [version 3 of test file1 +]) + +AT_DATA(file2.0, [version 0 of test file2 +]) + +AT_DATA(file2.1, [version 1 of test file2 +]) + +AT_DATA(file2.2, [version 2 of test file2 +]) + +AT_DATA(file3.0, [version 0 of test file3 +]) + +AT_DATA(file3.1, [version 1 of test file3 +]) + +AT_DATA(changelog.0, [first changelog entry +]) + +AT_DATA(changelog.1, [second changelog + +first changelog entry +]) + +AT_DATA(changelog.2, [third changelog -not on branch- + +second changelog + +first changelog entry +]) + +AT_DATA(changelog.3, [third changelog -on branch A- + +second changelog + +first changelog entry +]) + +AT_DATA(changelog.4, [fourth changelog -on branch C- + +third changelog -on branch A- + +second changelog + +first changelog entry +]) + +AT_DATA(changelog.5, [fifth changelog -on branch D- + +fourth changelog -on branch C- + +third changelog -on branch A- + +second changelog + +first changelog entry +]) + +AT_DATA(branchlist, [test +test.A +test.C +test.D +]) + +# build the cvs repository + +CVSROOT=`pwd`/cvs-repository +AT_CHECK(cvs -q -d $CVSROOT init, [0], [ignore], [ignore]) +AT_CHECK(test -e $CVSROOT) +AT_CHECK(test -e $CVSROOT/CVSROOT) +AT_CHECK(test -e $CVSROOT/CVSROOT/modules) + +# checkout the empty repository and commit some files + +AT_CHECK(cvs -d $CVSROOT co ., [], [ignore], [ignore]) +AT_CHECK(mkdir testdir) +AT_CHECK(cp file1.0 testdir/file1) +AT_CHECK(cp file2.0 testdir/file2) +AT_CHECK(cp changelog.0 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT add testdir, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT add testdir/file1, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT add testdir/file2, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT add testdir/changelog, [], [ignore], [ignore]) +AT_CHECK(cvs -d $CVSROOT commit -m 'initial import' testdir/file1 testdir/file2 testdir/changelog, [], [ignore], [ignore]) + +# commit first changes +AT_CHECK(cp file1.1 testdir/file1) +AT_CHECK(cp changelog.1 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT commit -m 'first commit' testdir/file1 testdir/changelog, [], [ignore], [ignore]) + +# now we create a branch A +AT_CHECK(cd testdir; cvs -d $CVSROOT tag -b A, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT up -r A, [], [ignore], [ignore]) + +# alter the files on branch A +AT_CHECK(cp file2.1 testdir/file2) +AT_CHECK(cp changelog.3 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT commit -m 'commit on branch A' testdir/file2 testdir/changelog, [], [ignore], [ignore]) + +# branch again into B +AT_CHECK(cd testdir; cvs -d $CVSROOT tag -b B, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT up -r B, [], [ignore], [ignore]) +# branch B is left untouched + +# go back to A and branch into C +AT_CHECK(cvs -d $CVSROOT up -r A -A, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT tag -b C, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT up -r C, [], [ignore], [ignore]) + +# add a file3 +AT_CHECK(cp file3.0 testdir/file3) +AT_CHECK(cp changelog.4 testdir/changelog) +AT_CHECK(cd testdir; cvs -d $CVSROOT add file3, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT commit -m 'commit on branch C' file3 changelog, [], [ignore], [ignore]) + +# branch into D +AT_CHECK(cd testdir; cvs -d $CVSROOT tag -b D, [], [ignore], [ignore]) +AT_CHECK(cd testdir; cvs -d $CVSROOT up -r D, [], [ignore], [ignore]) +AT_CHECK(cp file1.3 testdir/file1) +AT_CHECK(cp file2.2 testdir/file2) +AT_CHECK(cp file3.1 testdir/file3) +AT_CHECK(cp changelog.5 testdir/changelog) +AT_CHECK(cd testdir; cvs -d $CVSROOT commit -m 'commit on branch D' file1 file2 file3 changelog, [], [ignore], [ignore]) + +# and create some mainline changes after the branch +AT_CHECK(cvs -d $CVSROOT up -A, [], [ignore], [ignore]) +AT_CHECK(cp file1.2 testdir/file1) +AT_CHECK(cp changelog.2 testdir/changelog) +AT_CHECK(cvs -d $CVSROOT commit -m 'commit on mainline after branch' testdir/file1 testdir/changelog, [], [ignore], [ignore]) + +# import into monotone and check presence of files +AT_CHECK(MONOTONE --branch=test cvs_import --debug --verbose $CVSROOT/testdir, [], [ignore], [ignore]) + +# check if all branches were imported +AT_CHECK(MONOTONE list branches, [], [stdout], [ignore]) +AT_CHECK(cmp stdout branchlist) + +# checkout the imported repository into maindir and branchdir +AT_CHECK(MONOTONE checkout --branch=test maindir, [], [ignore], [ignore]) +AT_CHECK(MONOTONE checkout --branch=test.A branchA, [], [ignore], [ignore]) +AT_CHECK(MONOTONE checkout --branch=test.C branchC, [], [ignore], [ignore]) +AT_CHECK(MONOTONE checkout --branch=test.D branchD, [], [ignore], [ignore]) + +# check for correctness of the files in the main tree +AT_CHECK(cmp file1.2 maindir/file1) +AT_CHECK(cmp file2.0 maindir/file2) +AT_CHECK(cmp changelog.2 maindir/changelog) + +# check for correctness of the files in branch A +AT_CHECK(cmp file1.1 branchA/file1) +AT_CHECK(cmp file2.1 branchA/file2) +AT_CHECK(cmp changelog.3 branchA/changelog) + +# check for correctness of the files in branch C +AT_CHECK(cmp file1.1 branchC/file1) +AT_CHECK(cmp file2.1 branchC/file2) +AT_CHECK(cmp file3.0 branchC/file3) +AT_CHECK(cmp changelog.4 branchC/changelog) + +# check for correctness of the files in branch D +AT_CHECK(cmp file1.3 branchD/file1) +AT_CHECK(cmp file2.2 branchD/file2) +AT_CHECK(cmp file3.1 branchD/file3) +AT_CHECK(cmp changelog.5 branchD/changelog) + +# check the log of branch A for correctness +AT_CHECK(cd branchA; MONOTONE log, [], [stdout], [ignore]) +AT_CHECK(grep "beginning of branch" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "initial import" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "first commit" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch A" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch B" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "commit on branch C" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "commit on branch D" stdout, [1], [ignore], [ignore]) + +# check the log of branch C for correctness +AT_CHECK(cd branchC; MONOTONE log, [], [stdout], [ignore]) +AT_CHECK(grep "beginning of branch" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "initial import" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "first commit" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch A" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch B" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "commit on branch C" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch D" stdout, [1], [ignore], [ignore]) + +# check the log of branch D for correctness +AT_CHECK(cd branchD; MONOTONE log, [], [stdout], [ignore]) +AT_CHECK(grep "beginning of branch" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "initial import" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "first commit" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch A" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch B" stdout, [1], [ignore], [ignore]) +AT_CHECK(grep "commit on branch C" stdout, [], [ignore], [ignore]) +AT_CHECK(grep "commit on branch D" stdout, [], [ignore], [ignore]) + +AT_CLEANUP ============================================================ --- ChangeLog 7fbfacfd6eb7f8444712762f48501ae2b206ad6b +++ ChangeLog aab0cdb4b9f37d420fc9e71711fc97ccfb2f1fe4 @@ -1,3 +1,10 @@ +2006-02-17 Markus Schiltknecht + + * rcs_import.cc: make cvs_import connect branches by simply + comparing each revision with the branchpoint state needed. + If a branch can not be connected to any revision we fall + back to starting the branch from scratch as before. + 2006-02-17 Matthew Gregan * sqlite/*: Import SQLite 3.3.4. ============================================================ --- rcs_import.cc 0de2b62e2c973f4c94592cff4990f0a43eb70294 +++ rcs_import.cc e286ee7238e5811f587dba45a3b43b71900fb704 @@ -83,15 +83,18 @@ { bool has_a_branchpoint; bool has_a_commit; + bool has_parent_rid; time_t last_branchpoint; time_t first_commit; + revision_id parent_rid; map live_at_beginning; - vector lineage; + vector lineage; cvs_branch() : has_a_branchpoint(false), has_a_commit(false), + has_parent_rid(false), last_branchpoint(0), first_commit(0) { @@ -1031,6 +1034,7 @@ ticker & n_revs); void consume_cluster(cvs_cluster const & c); + void check_for_branchpoints(void); void add_missing_parents(split_path const & sp, cset & cs); void build_cset(cvs_cluster const & c, cset & cs); void store_auxiliary_certs(prepared_revision const & p); @@ -1064,9 +1068,6 @@ cluster_consumer cons(cvs, app, branchname, *branch, n_revs); unsigned long commits_remaining = branch->lineage.size(); - // step 1: sort the lineage - stable_sort(branch->lineage.begin(), branch->lineage.end()); - for (vector::const_iterator i = branch->lineage.begin(); i != branch->lineage.end(); ++i) { @@ -1225,20 +1226,20 @@ ticker n_revs(_("revisions"), "r", 1); - while (cvs.branches.size() > 0) + // first, sort the lineages of the trunk and all branches + L(FL("sorting lineage of trunk\n")); + stable_sort(cvs.trunk->lineage.begin(), cvs.trunk->lineage.end()); + for(map >::const_iterator i = cvs.branches.begin(); + i != cvs.branches.end(); ++i) { - transaction_guard guard(app.db); - map >::const_iterator i = cvs.branches.begin(); string branchname = i->first; shared_ptr branch = i->second; - L(FL("branch %s has %d entries\n") % branchname % branch->lineage.size()); - import_branch(cvs, app, branchname, branch, n_revs); - // free up some memory - cvs.branches.erase(branchname); - guard.commit(); + L(FL("sorting lineage of branch %s\n") % branchname); + stable_sort(branch->lineage.begin(), branch->lineage.end()); } + // import trunk first { transaction_guard guard(app.db); L(FL("trunk has %d entries\n") % cvs.trunk->lineage.size()); @@ -1246,6 +1247,36 @@ guard.commit(); } + while (cvs.branches.size() > 0) + { + transaction_guard guard(app.db); + map >::const_iterator i; + shared_ptr branch; + + // import branches in the correct order + for (i = cvs.branches.begin(); i != cvs.branches.end(); ++i) + { + branch = i->second; + if (branch->has_parent_rid) break; + } + + if (i == cvs.branches.end()) + { + L(FL("no more connected branches... unconnected import\n")); + i = cvs.branches.begin(); + branch = i->second; + } + + string branchname = i->first; + + L(FL("branch %s has %d entries\n") % branchname % branch->lineage.size()); + import_branch(cvs, app, branchname, branch, n_revs); + + // free up some memory + cvs.branches.erase(branchname); + guard.commit(); + } + // now we have a "last" rev for each tag { ticker n_tags(_("tags"), "t", 1); @@ -1278,8 +1309,51 @@ n_revisions(n_revs), editable_ros(ros, nis) { - if (!branch.live_at_beginning.empty()) + if (branch.has_parent_rid) { + parent_rid = branch.parent_rid; + app.db.get_roster(parent_rid, ros); + + L(FL("starting cluster for branch %s from revision %s which contains:\n") + % branchname + % branch.parent_rid); + + // populate the cluster_consumer's live_files and created_dirs according + // to the roster. + node_map nodes = ros.all_nodes(); + for (node_map::iterator i = nodes.begin(); i != nodes.end(); ++i) + { + shared_ptr node = i->second; + + if (is_dir_t(node)) + { + split_path dir; + + ros.get_name(node->self, dir); + L(FL(" dir: %s\n") % dir); + safe_insert(created_dirs, dir); + } + else if (is_file_t(node)) + { + std::string rev; + std::string name; + cvs_path path; + split_path sp; + + ros.get_name(node->self, sp); + file_path fp(sp); + path = cvs.path_interner.intern(fp.as_internal()); + + dump(downcast_to_file_t(node)->content, rev); + + L(FL(" file: %s at revision %s\n") % fp.as_internal() % rev); + live_files[path] = cvs.file_version_interner.intern(rev); + } + } + } + + else if (!branch.live_at_beginning.empty()) + { cvs_author synthetic_author = cvs.author_interner.intern("cvs_import"); @@ -1324,7 +1398,36 @@ } } +void +cluster_consumer::check_for_branchpoints(void) +{ + for(map >::const_iterator i = cvs.branches.begin(); + i != cvs.branches.end(); ++i) + { + string branchname = i->first; + shared_ptr child_branch = i->second; + if ((!child_branch->has_parent_rid)) + { + L(FL("comparing revision with branchpoint for branch %s (%d to %d files)...\n") + % branchname + % live_files.size() + % child_branch->live_at_beginning.size()); + + if (live_files.size() == child_branch->live_at_beginning.size()) + if (live_files == child_branch->live_at_beginning) + { + L(FL("setting branchpoint for branch %s to revision %s\n") + % branchname + % parent_rid); + + child_branch->has_parent_rid = true; + child_branch->parent_rid = parent_rid; + } + } + } +} + void cluster_consumer::store_revisions() { @@ -1461,4 +1564,6 @@ preps.push_back(prepared_revision(child_rid, rev, c)); parent_rid = child_rid; + + check_for_branchpoints(); } ============================================================ --- testsuite.at 7f433b14b3bd2a67eeacc49577a64e35d97ed02f +++ testsuite.at c5cc87a93f09292b84428622d48f099b96d8b8a1 @@ -764,3 +764,5 @@ m4_include(tests/t_check_db_format.at) m4_include(tests/t_rename_destdir.at) m4_include(tests/t_ls_changed.at) +m4_include(tests/t_cvsimport_branch.at) +m4_include(tests/t_cvsimport_branch2.at)