SourceForge has been redesigned. Learn more.


CVS is being phased out. We only offer limited support for CVS for projects previously using it on the Classic SourceForge system, with commit access going away at the end of November, 2017.

We recommend that projects upgrade to a more modern SCM, like Subversion, rather than using CVS. CVS has limitations which newer SCM solutions have been designed to overcome. We continue to support CVS for those projects who decide that CVS adequately supports their needs.


Backups of a CVS repository may be made using rsync.

Example (replace PROJECTNAME with the UNIX group name of your project):

rsync -av rsync://\* cvs


We encourage all CVS projects to convert to Subversion, aka SVN, since it is most similar to CVS and also has the ability to checkout just a subdirectory of your new repository. Other people may want to choose Git, which is more distributed but has a steeper learning curve (note that each Git repo you create is always checked out in full). You can convert your repo using one of our interactive shell instances or a host of your own.

For either method, the first step in the conversion is to use the project admin page on to create an empty repository in the style of your choosing (Subversion, Git, etc.).

Note: if your cvs repo is still visible in your menus, it will be occupying the "code" path (aka "mount point") in all the browse/checkout URLs, so your new repository will need to use a different mount name. You can avoid this issue by first using the "Delete Everything" menu item in the menubar (unlock the menu and use the cog next to cvs). Even though the menu says "Everything", for the legacy cvs data the delete action only deletes the mount point, freeing up the use of the "code" path in the site URLs (it doesn't affect accessing the cvs data in any way). The choice is yours if you want to go ahead and hide cvs on the menu-bar early, or use an alternate name (e.g. "code2") for the conversion. We document how to rename this path later in this document, but it does involve manually copying and deleting your repo to do that.

If you want to do the conversion on your own machine, start by making a backup copy of your cvs data (so that the conversion isn't bogged down by the remote connection). See the backup instructions above.

If you’d like to instead use an interactive shell for the conversion, request that one be created for you:

 ssh -t create

When the shell finishes its creation, you will be able to find a read-only version of your cvs repository in the directory /home/cvs/P/PR/PROJECT (where the upper-case letters are taken from your project name). This cvs data is the same replicated copy of your master repo that the pserver hosts use, so if you’ve just committed a change, make sure that you give it a few minutes for the change to make it into the nfs copy.

You will also be able to find your new, empty repository in a directory based on its type, but using a path element of “p” (for project) instead of the P/PR hash directories. For instance, /home/svn/p/PROJECT/code (the “code” directory is typical, but you may have chosen a different name when you created it). If you’re converting on your own machine, checkout the new (empty) repo so that you can populate it and push it back to the server when you’re done. Keep in mind that only the SCMs that existed at the time of the shell's creation will be visible to you, so be sure to create your new repo before creating your interactive shell (or shut down the shell and create it again to see a new repo).

The interactive shell service provides several conversion tools, including cvs2svn, cvs2git, and git-cvs (used by the “git cvsimport” command).

Converting to Subversion

If you choose to use cvs2svn to convert your repository, you have the choice of converting the entire cvs area or (with the use of a config file) converting a single section of the cvs data at a time (to divide things into several new repos). To convert the whole thing, you can run command similar to this:

cvs2svn --tmpdir ~/cvs2svn-tmp --existing-svnrepos --retain-conflicting-attic-files \
  --encoding=utf8 --encoding=latin1 --fallback-encoding=ascii \
  -s /home/svn/p/PROJECT/code /home/cvs/P/PR/PROJECT

If you are converting on your own computer, you'll need a local copy of your cvs repository (see the backup instructions). Then run the above command into a non-existing directory without the --existing-svnrepos option (so get rid of that option in the above command and change the /home/svn/... path to a local path on your computer that you want cvs2svn to create). Once the conversion is done, you can use svnadmin to export the data and use our standard SVN import capabilities.

Run “cvs2svn --help” for a summary of the available options. The web has more detailed documentation:

For more intricate conversion options, including converting just a part of the cvs repo into the new repo, you’ll need to create a config file. The documentation above covers that and more. Keep in mind that Subversion allows you to checkout just a portion of a repository, so if you convert all of your CVS data into one SVN repo, you will still be able to checkout any subdirectory in the repo that you want -- there will be just one "log" of changes, though, which some projects prefer.

An interactive shell only stays running for 4 hours, so make sure you have enough time left before you start a conversion (see the “timeleft” command). Be sure to save any config file you might be editing in your home directory, not in /tmp (so that is saved between shell instances). For larger projects this won't be enough time, so either convert it on a local host or ask us for assistance. As an example, one project with 1.2G of data took 10 hours to convert into 95267 SVN revisions on an interactive shell host (with the auto-restart disabled).

If you do a conversion on your local host using cvs2svn, you can import the final result using either an rsync copy of the svn repo (NOT an svn checkout!), or via our normal SVN Import instructions. For those not familiar, the svn repo has top-level directories named conf, db, hooks, and locks. An svn checkout has your commited files and a top-level .svn dir.

The following example uses rsync to send your completed svn repo to our remote repo using an interactive shell. It excludes the hooks directory to avoid changing the existing hook files that we create for you. The first command creates a remote shell where the ls -l is used to find all your svn repo names, at which point the exit takes you back to your local system for the copy. Keep in mind that the trailing slashes on an rsync command are very important, and that the USERNAME and PROJECT/code strings should be customized as needed.

ssh -t create
ls -ld /home/svn/p/*/*
rsync -ai --del --exclude=/hooks/ \

Once either conversion method finishes running, go to the web page for your SVN repo. It will say "The metadata for this repository is missing. To fix, please try a refresh." Click the "Refresh Repository" link in the repo's admin drop-down for it to detect & process all the new commits (look for the blue triangle in the sidebar). If your repository is very large, this may take some time. The repo is usable immediately for svn checkouts & checkins, even while the web view is processing the commits.

Converting to Git

Some folks prefer to use cvs2git, and some prefer to use "git cvsimport". I believe that cvs2git does a better job, so we'll discuss that one first. Be sure to create your (empty) git repo via the web admin pages and then create your interactive shell once it exists. Also note in the following commands that you'll want to change the FIXME to a username to use for synthesized commits, change the P/PR/PROJECT path to be based on your project name, such as f/fo/foobar, and the "code.git" dir might be named differently, depending on your git mount point):

mkdir cvs2git-tmp
cd cvs2git-tmp
cvs2git --blobfile=blob.dat --dumpfile=dump.dat \
    --username=FIXME --default-eol=native \
    --encoding=utf8 --encoding=latin1 --fallback-encoding=ascii \
timeleft # you may want to shutdown & re-create the shell if time is short
cat blob.dat dump.dat | git --git-dir=/home/git/p/PROJECT/code.git fast-import
rm blob.dat dump.dat

At that point you should be able to check-out your git using the standard git checkout instructions on the website.

If you have a really big cvs repo, you'll probably need to run the conversion locally with a backup copy of your cvs repo (see above) and with a fresh git checkout from SourceForge (of your new empty git repo) that you can push when you've updated it. So, in the above instructions, you would specify the path to your cvs backup dir instead of /home/cvs/P/PR/PROJECT, and then, instead of the above command you would run this:

git checkout ssh:// new_git
cat blob.dat dump.dat | git --git-dir=new_git/.git fast-import
cd new_git
git checkout
git push origin --mirror
cd ..
mv new_git /a/better/path/for/your/new_git
rm blob.dat dump.dat

If you need more info, visit the cvs2git documentation page.

A simple way to convert your repo to Git is to use the “git cvsimport” command. For the best conversion result, it helps to create an author.txt mapping file that has the mapping of usernames to the extra user information that Git includes. The file should contain lines that look like this:

username = User Name

The following command can provide you with a list of all the author usernames:

for vfile in `find /home/cvs/P/PR/PROJECT -name '*,v'`; do
  rlog $vfile | sed -nr 's/^date:.* author: ([^;]+).*/\1/p'
done | sort -u >~/cvs-author-names

Be sure to verify the resulting list -- you’ll probably want to at least remove “root”. You can then turn that list of names into an authors.txt file like this:

for uname in `cat ~/cvs-author-names`; do
  fname=`echo "$json" | sed -nr 's/\{"username": "[^"]+", "name": "([^"]+)".*/\1/p'`
  echo "$uname = $fname <$>"
done >~/authors.txt

Be sure to scan the resulting ~/authors.txt file for missing Full Names and make any other changes you want.

Because the cvsimport command wants to create locks inside the read-only cvs dirs, you’ll need to create a copy of your repo for the conversion. If you’re using our interactive shell service you can do a copy like this (note the trailing slashes):

rsync -aiv /home/cvs/P/PR/PROJECT/ ~/cvs/

Also note that the new git dir must not exist yet (you’ll push the result into the project’s “bare” repo later).

So, to do the conversion you can run this:

rm -rf ~/new-git
git cvsimport -v -a -k -d ~/cvs -C ~/new-git -A ~/authors.txt MODULE

The MODULE name is one of your top-level cvs directories. It is very often the same name as the project name.

When that is done you can review the repo’s commits (“git log”), tags (“git tag”), and branches (“git branch -a”) and make any needed tweaks prior to making it public (e.g. removing unneeded tags and/or branches).

When you’re all set, we need to push the resulting git repo to the shared “origin”. Do NOT just copy the new-git dir into your /home/git subdirectory! The “new-git” directory contains a working tree, while the directories under /home/git are all “bare” format (without a working tree). To publish the result you can use one of the following “git push” commands.

If you are using our interactive shell service, run this (keep in mind that your PROJECT may have a different mount point than the default "code.git" dir mentioned here):

git push /home/git/p/PROJECT/code.git --mirror

You can then do a checkout from home of your git repo (see the normal instructions on your project’s git info page) and verify that all is well.

Don’t forget to remove ~/new-git and ~/cvs dirs once you are done with them.

If you are doing a remote conversion, you’ll want to setup the right origin in your new-git dir and then push (note that you typically omit the trailing “.git” from the “code.git” dir):

git remote add origin ssh://
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
git push origin --mirror

That sets up your new git dir to be associated with the remote git repo, just as if you had done a clone. On subsequent pushes you won’t need to specify the "origin" or "--mirror" part of the push, since git will know the association between the remote and local branches.

See also:

After you push your changes, the web view of the repository may take some time to process the commits if your repo is very large. While it does that, the repo is still usable for git checkouts and commits.

Remove the CVS entry from your project

If you did not do so earlier, you should now use the site's admin pages to "Delete" the CVS repository. Note that this deletion just removes the CVS entry in the site's menu's (unlike other repository deletion actions), and your access to the CVS data will still be availible (read-only) on into the future, if you need it.

Renaming your Code Mount Point

Most projects had their CVS repository available at their mount point on the site. Your new repository will thus be at a different path during the conversion process. If you would like change the site URLs (both for web browsing and for checkouts) to have the "code" path in it again, you can get it done but it takes a little bit of trickery:

First, make sure that your CVS mount is gone (since the "code" location needs to be freed up). Then, create a new repo in the same style as your converted repo (e.g. subversion, git, or whatever) -- when creating this repo, make sure that it has the "code" mount point that you want to get back to. Once that is created, you can use an interactive shell (see above) to copy the underlying repo data from the new mount point to the code mount point. With that done, make sure that you can check-out your new repo. It is also a good idea to make a backup of your new repo, to be extra safe. When you are comfortable with your new repo's "code" location, you can then use the site's admin pages to delete the non-code mount version of your repo.


Documentation: SSH Key Fingerprints