CVS is best described as an inconsistent revision control tool.  There are a
large number of design flaws w/in CVS with regard to how CVS records history
that make it nearly impossible to properly translate history in an automated
fashion.

  * CVS is not atomic.  If a user starts to commit changes, they are allowed to
    stop the commit part way through.  Those portions of the commit that made
    it to the server stay, those portions that did not must be re-committed as a
    separate commit.  This scenario can also be triggered by stale locks on the
    server side, or in the event of a conflict between changes from the client
    and existing data on the server.

  * CVS has no mechanism for tracking changes to the repository as a whole.
    All changes occur as deltas on a per-file basis, so getting any
    understanding of what about the repository changed on a commit-per-commit
    basis is very difficult.

  * CVS does not support a mechanism for adding files exclusively to a branch.
    When a new file is added to a branch, it must first be added to the main as
    a base revision, and then a branch revision made from there.  This branch
    revision is then tagged to be a member of a branch of development.  This
    makes it so that odd files may appear in the MAIN branch unexpectedly, and
    sometimes when they are not wanted.  Unfortunately, it seems 90% of CVS
    usage deals with development on the MAIN.

  * Originally, RCS distinguished revisions on a branch vs the revisions for
    branches themselves based on the number of levels of versions.  A revision
    for a file delta always had an even number of version levels, where a
    branch revision always had an odd number. i.e.  x.x = file delta revision,
    x.x.x = branch revision.  For some reason, the CVS developers felt there
    was a "performance increase" gained by adding a concept called "magic
    branches", where a branch would be recorded normally, but a revision on a
    branch would use the branch revision with a .0 appended, and then supply
    the revision for the file.  This sort of revisioning process only seems to
    occur for branches that are not an immediate VENDOR branch though, making
    branch tracking extremely problematic.  This also generates an
    incompatibility with RCS.  So far as I can tell, there is no real
    performance gain of checking to see if the revision ends in '.0.x' vs
    counting the number of '.' in the revision to decide if it is a file-delta
    or a branch.

  * CVS utilizes a questionable method for deciding if a file has been deleted
    or not.  When a file is marked for deletion in one branch, it is moved into
    a sub-directory called 'Attic'.  On another branch though, said file may
    still be valid, and thus the file must be moved out of the Attic.  I expect
    that this is 2 different solutions to the same problem given CVS's
    evolution as RCS originally had no way to mark a file dead, so CVS
    originally moved it into the Attic/.  But the problem would naturally arise
    of "what if they re-add the dead file" .. so the implemented the stated a
    dead state into a file to show that a particular revision was marked dead.
    Personally, they should have removed the attic once they incorporated the
    dead state to simplify things, but CVS isn't about simplicity it seems.

  * CVS has no method of tracking revisions to directories themselves.  This is
    particularly annoying when dealing with move/renames.  One of the best
    solutions I have been able to think up for this problem is to look for
    files removed and then added w/in the same changeset.  If the files are
    similar, i.e. they both have the same md5sum, then we can assume it was a
    rename.  With renaming directories we end up needing to check the contents
    of the directories to see if a collection of files where removed, and then
    re-added in a new directory.  It is all very very cumbersome in the end, and
    prone to error, one of the reasons I am still wary of implementing it.

Attempting to take CVS history and generate a pseudo-atomic view of it is, for
the most part, simple though tedious.  The basic idea is to take every
revision of every file and place them into an array.  The array then needs to
be sorted from oldest revision to newest.  During the sorting it is,
unfortunately, necessary to check to see if 2 revisions have the same
timestamp, if they do then it is possible one revision is a branch of another
that was generated when a file is added to a branch, such as during an import.
Once all the revisions are sorted the list is traversed, beginning to end,
checking to see if the revisions log and author match.  There is a cute problem
here in that CVS allows 2 commits to occur simultaneously, so one can not
assume that the changeset is done when a revision is run into that is not part
of that changeset.  The solution here is to track changesets into arrays for each
author.  If the new revision has the same log as the last changeset in that
authors changeset array, then that revision is part of the same changeset, if
not, then a new changeset is created and attached to the end.  We basically assume
that one author can not commit to the same repository at the same time (which
in and of itself may be a poor assumption as well).

Tags are handled when clumping revisions into changesets.  With each revision
added to a changeset, we check to see if the revision was tagged.  If so, then
we flag that changeset under that tag in a tag dictionary.  Since each revision
is sorted in order, the last revision, and thus the last changeset, with a tag
is presumably the tagged changeset.  The 'lint' subcommand can be used to
validate tag consistencies by traversing the head of the entire repository at
that changeset and checking to validate that all revisions have the tag.

All changesets exist on a branch.  If no branch name exists, it is assumed to
be on the MAIN, though admittedly this is a bad assumption as CVS allows a
user to remove a branch tag from the repository, effectively orphaning a branch
and making it extremely difficult to get back.

The I-Just-Want-To-Use-It-To-Sync-With-Arch-Section

Ok, be warned though, this -will- commit changes to your tla archive. If you
encounter a bug... well, you were warned.

1) Build a cache
   in your checked out CVS dir, that matches an arch project tree somewhere..
   (It could be the same location):
   $ cscvs cache -b
   .. wait ..

2) Identify the changesets you want to import to arch (or tla)
   in the same location:
   $ cscvs log -PBRANCH | less
   where BRANCH is MAIN for the 'HEAD' tag in CVS, or the name of the specific
   branch you are syncronising with.
   look through the logs until you identify where you need to start bringing in
   changes from

[ if creating a new arch repository ]
3) Import the first changeset you wish to include in your arch repository.
   cd /home/src
   mkdir import-tree
   cd import-tree
   tla init-tree FULLY_QUALIFIED_ARCH_VERSION
   tla archive-setup FULLY_QUALIFIED_ARCH_VERSION
   tla add-log-version FULLY_QUALIFIED_ARCH_VERSION
   edit your {arch}/\=tagging-method file appropriately.
   cd CVSSOURCEDIR
   $ cscvs totla BRANCH.X /home/src/import-tree
   cd /home/src/import-tree
   tla tree-lint
   check everything is ok.
   tla import
   note that "BRANCH." is optional if you're referring to MAIN

4) Commit newer changesets to the arch tree
   from the CVS SOURCE DIR.:
   $ cscvs totla -c BRANCH.X: PATHTOARCHPROJECTTREE
   where X is the first changeset on the branch your arch project tree tracks
   which needs to be merged into tla. If you just imported changeset 1 in step
   3, you'll want to use changeset 2 here. PATHTOARCHPROJECTTREE is... well,
   what the name implies.
