One of my clients uses a CVS repository for all its source code. People recognize that there are better options available than CVS, but it's been cranking along fine for 15 years, and they see no compelling reason to change.
However, I really like being able to commit incremental changes often in my own personal branches, and while not connected to the company network (I work from home). So I've been checking out files from the CVS repository, using Git locally to manage modifications, and then periodically committing those changes back to the remote CVS repository.
I figured I'd write up what I'm doing, in case others want to try the same thing, or others can tell me a better way to do what I'm doing. I'm still a bit of a Git newbie, so if I'm doing something stupid, please let me know. I'm assuming the reader has a basic understanding of CVS and Git. If not, see the CVS manual and/or the Git tutorial. I predominantly work on Windows, but I use PowerShell, so there is no difference between the commands I use on Windows and those I use on Mac OS X or Linux.
In the examples, I'll use a few variables:
USER=myusername CVSROOT=:pserver:$USER@cvsserver:2401/cvsrepository PROJECT=myprojectname DROPBOX=mydropboxfolder
($DROPBOX refers to my Dropbox directory, where I keep all sorts of little files that I want synched to all my computers. If you haven't looked at Dropbox, do.)
First, we need to get the CVS repository. Git does have a git-cvsimport command that I could use to suck all the CVS stuff into a local Git repository, but the CVS repository is huge, so that would be very slow, and frankly, I don't really trust
git-cvsimport. So I just do what I would normally do to get stuff from CVS:
cvs login cd ~/work cvs checkout $PROJECT
Next, I set up the local Git repository:
cd ~/work/$PROJECT git init
The working directory contains a lot of files that I don't want or need to track with Git, so I've got a standard
.gitignore file that looks like this:
CVS .#* .hg .hgignore bin obj Debug Release TestResults *.obj *.suo *.ncb *.user *.tli *.tlh *.idb *.pdb build *.pbxuser *.perspectivev3 .DS_Store xcuserdata *.old *.log *.out *.cache
I just copy it from my Dropbox:
cp $DROPBOX/.gitignore .
Now, I'm ready to import everything into the Git repository:
git add . git commit -m "Initial commit"
I keep a local tag
cvssync that indicates the last time that the Git and CVS repositories matched.
git tag cvssync
Now I'm ready to do some work. I always want the
master Git branch to match CVS, so I create a topic branch where I do my work:
git checkout -b develop
After very efficiently and productively adding all the error-free code needed to implement whatever I'm implementing, I'm ready to commit on my
git commit -am "Implemented the whosey-whatsit"
I tend to commit changes frequently. If I go more than an hour or so without a commit, I get worried. The great thing about frequent commits is that it is easy to undo things. The bad thing about frequent commits is that your commit history has lots and lots of entries, but as nobody is going to see that history but me, I don't worry about that.
Now my boss calls and asks when the thing is going to be ready, and I tell him I'll check it into CVS right away.
First, I quit Visual Studio or whatever editor(s) I'm using, because the next few operations will cause the contents of files to change, and IDE's often don't handle that gracefully. This step also ensures that I don't forget to save all my changed files.
I have to merge the
develop branch back into the
git checkout master git merge develop
Then, I pull in whatever changes others have added to CVS
cvs update -d git commit -am "Sync with CVS"
(I skip the
git commit here if there were no changes from CVS.)
I check what I've changed since the
cvssync tag, to remind myself and to verify it is right:
git log cvssync.. cvs diff
Finally, I can push my changes to CVS:
cvs commit -m "Implemented the whosey-whatsit"
My employer likes to put the
$Id$ tag into source files, so after a
cvs commit, any committed files are going to have new identifiers, so I need to commit those changes to my
git commit -am "Sync with CVS"
I update my
git tag -f cvssync
If, while I'm doing my work, others check things into CVS, and I need those changes, here is what I generally do (after quitting Visual Studio):
git commit -am "Check in work branch" git checkout master cvs update -d git commit -am "Pull from CVS" git checkout develop git rebase master
It may seem like a lot of work, but it's saved me a couple of times already. I have some aliases and scripts that automate some of the steps, so my actual workflow is not as verbose as what I've written here.