Friday, July 10, 2009

In my last post I talked a little about my experience starting to learn to use Git. Now I'm going to talk about what I'm doing with it currently in an environment where Git can't be used as the end-to-end version control system (at least not immediately).

The Situation

At work we're using CVS for a legacy project that I'm working on. It's been around a while and the practices with it are pretty set. Branches are used for release versions of the product and that's about it. I tend to have a feature or two I'm working on and I also occasionally do something experimental. The trouble is, getting changes related to separate things mixed together can make it confusing to know what needs to be committed and what doesn't when it comes time to commit something (committing early and often would help with this, but it's not quite how things are done). Especially problematic would be if changes related to different things affected the same file.

To deal with this, I'd sometimes check out a separate copy of the project into another directory and work on something specific there, but this wasn't very efficient. I'm programming in Java using Eclipse, and in addition to the issue of keeping track of multiple complete copies of the project, I'd have to switch workspaces or open up another copy of Eclipse to work with those copies. This wasn't very efficient.

The Solution

When I started looking into Git and experimenting with it, I realized that I could probably use it locally with a totally different VCS acting as the central repository. Git stores all its data in a single folder in the root of the working directory, which means that its files aren't mixed into your entire directory structure the way they are with CVS and its CVS subdirectories in every directory.

Here's the process I used to get this project set up for work with Git:

Check out a clean copy of the project into a new directory to use.

Get the environment all set up for normal use in Eclipse.

Do git init to create the Git repository at the root of the project.

Create and set the .gitignore file, having it ignore compiled code output folders, among other things. (Note: It should also ignore all CVS folders.)

From there, use Git normally... create a development branch, and branches off that for features, experiments, etc.

When you're ready to commit something to CVS, merge the changes all the way down to the master branch and commit in CVS.

When I switch branches, I just refresh the project in Eclipse and it immediately reflects the branch I'm now on. Since the files that exist may differ between branches, I also like to use tasks in Mylyn. I have tasks that are specific to whatever I'm doing on a specific branch, and I activate them while working on them. I'd done a little of that before, but it's even more useful in this situation. If I'm working on some files and need to switch to another branch that doesn't have them, I just deactivate the task and the editor tabs go away. I switch branches, do whatever I need, and then when I switch back and activate the task. Despite the fact that the files ceased to exist on the files system for a while, when the task is reactivated the files are right back there like before!

This approach has made it far easier and more fun to work on multiple separate things at once, and it's easy to do even though the central repository is the ancient CVS! Thanks for being awesome, Git!

Update:

How you interact with CVS when doing this is important. First, the .gitignore file should be set to ignore all CVS folders! You don't want changes to the files in the CVS folders to have to be committed to your Git repository. You want to ignore the fact that you're also using CVS as much as possible. Given that, you should also only ever update from CVS or commit to CVS from your master branch! Things can get really weird if you update or commit in a branch you've created.