You ever have an old car that you refuse to relinquish because it continues to get 10 miles to the gallon and you've had so many memories with it? Sure, you've seen the commercials for those fancy cars that are more environment friendly, safer to drive, and have a built-in GPS, but you still refuse to surrender old "Eleanor" for some new technology. Until recently, I've been on that same bus only my car was Subversion. I've been using Subversion for the past two years as my Source Code Management (SCM) system of choice. Just recently, I spent over three hours dealing with a corrupt Subversion repository. This single event was the straw that broke the camel's back, in fact, it didn't just break the back, it smashed the camel into a little pancake. Therefore, I decided to 'Git' rid of Subversion and give Git a shot at the title.
For me, managing source code is important. At the time when I selected Subversion, I really didn't know much about SCM tools and basically adopted what the majority of the crowd seemed to be using. It integrated well into my development environment, played nice with Capistrano for deployment, and was a tad bit better than my previous backup strategy of simply copying entire directories and labeling them sequentially with timestamps.
As a young buck, my source code would typically reside in a single directory that was backed up occasionally, nothing automated. Deployment was very manual and consisted of copying my working development directory onto the production server or better yet, I might have just edited the production code directly. Fortunately, my development practices have gradually evolved over the years to accommodate a more robust approach to managing the software development life cycle. Gradually, I gravitated towards maintaining a development and production environment where code was managed using a version control system, Subversion, and deployment consisted of the 'rake release' command.
While Subversion certainly helped me get off the ground with managing source code, it didn't come without its own share of limitations. Mileage may vary on whether or not these are features or limitations, but here are a few shortcomings I found with using Subversion for my development workflow.
- Hidden .svn folders - To maintain state, Subversion places a .svn directory in every directory within your project. Quit it! Really, I don't want every directory to have some local state folder, can't se do something about this?
- Branching and tagging - Those familiar with Subversion are accustomed to the convention of tags, branches, and trunk. You create an entire copy of the working trunk and then place it into some other directory and call it a tag or a branch. Bah!
- Repository corruption - This may my own fault, but I had to deal with several corrupt repositories over the last two years and considering the amount (or lack there of) of development I do, I just couldn't imagine having to deal with this on the regular basis.
- Online or nothing - I like the idea of a centralized repository, but didn't like the fact that I had to be online to connect to it and commit changes. I wanted the ability to work from anywhere, online or offline, and commit changes as I deemed necessary.
These are just a few of the reasons I decided to explore alternative source code management systems. Did I really need something different? Probably not, but I decided to give Git a whirl since it had been receiving a fair bit of attention in the development community. Cmon, what's the worst that could happen anyway, I'm the only developer.
Source Code Management Models
What's Git?
According to their website, Git is a distributed version control system focused on speed, effectivity and real-world usability on large projects. Specific features include distributed development, strong support for non-linear development, handling of large projects, support for cryptographic authentication of history, and toolkit design.
That's great, but what really caught my attention is the ability to work in a disconnected and non-linear manner with strong support for branching and tagging. Not to mention that I'm typically an early adopter or at least wiling to give something a shot (i.e., Ruby on Rails) when it's receiving enough public attention.
Transitioning from Subversion to Git
I had two Subversion repositories that I needed to import to Git. Surprisingly enough, Git supports what I would call a fairly painless means to import a Subversion repository (including it's history) directly into git with a handy utility called git-svn. Included below are the general steps I used to convert one of my Subversion repositories to Git, results may vary.
Step 1: Import the subversion repository
First, I needed to create a new git repository. The command below essentially creates a bare git repository and imports the subversion repo.
git-svn init -T <repository url or path>
git-svn fetch
Step 2: Re-apply the tags
When you import from subversion, it will detect tags and branches, but does not automatically apply the tags. You need to search through the commit logs to find the commits that were used to apply your tags and then re-apply them using git. For me, I had four tags in Subversion that I had to reapply. To apply these tags, you use the git-tag command. For the sake of maintaing an accurate commit history, I also used the GIT_COMMITER_DATE variable I grabbed from when the commits were originally made for each tag.
GIT_COMMITTER_DATE="2007-09-25 00:17" git tag -a -m "Tagging the 1.0 release of www.epartment54.com" release-1.0 473a7403fc29e64a310f58ced52f1cdecb40087d
GIT_COMMITTER_DATE="2007-12-08 06:37" git tag -a -m "Tagging the 1.0 release prior to Rails 2.0 upgrade of www.epartment54.com" release-1.0-RoR-1.2 e08baa26816f9546f133808eaed5775907a482f5
GIT_COMMITTER_DATE="2007-12-21 18:28" git tag -a -m "Tagging the 1.1.0 release of www.epartment54.com" release-1.1.0 d83073ec35acc69b355833236109570af53119c4
GIT_COMMITTER_DATE="2008-04-17 18:23" git tag -a -m "Tagging the 1.1.1 release of www.epartment54.com" release-1.1.1 04b3f0b4cd7cdc37860257e76fc66c5c4ca860da
Step 3: Setup your .gitignore file
The .gitignore file specifies files you want git to intentionally not track (e.g., log files, pids, etc). Since I use the RoR framework, I had a few of these. I configured a root level .gitignore file in my project directory that designated files I wanted to ignore. I then placed empty .gitignore files in those directories, which basically results in git tracking the directory folders, but nothing inside them. The contents of my root level .gitignore file are as follows:
log/*.log
log/*.pid
log/*.out
tmp/**/*
public/images/simple_captcha/*.jpg
index/**/*
tmp/pids/*.pid
.DS_Store
doc/api
doc/app
touch log/.gitignore
touch tmp/.gitignore
touch public/images/simple_captcha/.gitignore
touch tmp/pids/.gitignore
Step 4: Add the files to the index and commit
I won't be covering the magic of the index file, but let's go ahead and add our changes to the index and then commit them to the master branch.
git add *
git commit -m "Adding some standard gitignore files"
Step 5: Setting up to sync with the remote repository
When using git, you have a complete copy of the repository at your disposal. Typically, you sync back upstream and merge your changes so that others (well, not in my case) can have the latest changes. I won't go into detail on how to setup your own remote repository, as there are plenty of tutorials already available. Just Google for "git remote repository".
git remote add origin ssh://<username>@<hostname>/<path to repository>.git
The command above essentially modifies our configuration to add the remote repository named origin and allow us to fetch updates from it to sync to our local repository.
git push origin master --tags
The command above essentially pushes our local repository to our bare remote repository. It says push the master branch (trunk in most cases) to our origin remote repository and make sure you include all the tags we re-applied as well.
At this point, I essentially had a local and remote repository that were in sync. Everything from here on out was a matter of git-status, git-add, git-commit, and git-log.
Capistrano deployment recipe
Remember, prior to this I was using Subversion to manage my code and with Git now in play, I needed to update my deployment recipe for Capistrano. It was a bit tricky at first finding out what parameters to include, but fortunately luck was on my side this time and I got it all working. The following is an excerpt out of my Capistrano deployment recipe using ssh and public key authentication.
default_run_options[:pty] = true
set :scm, :git
set :repository, "ssh://<username>@<hostname>/<path to repository>.git"
set :repository_cache, "git_cache"
set :ssh_options, { :forward_agent => true }
set :branch, "master"
set :deploy_via, :remote_cache
Alright, so that covers about everything I can think of for now. The rest will be a work-in-progress section that lists the good and the bad with regard to my experiences with Git over the upcoming weeks.
Pros
- All in one place. The entire repository is on my system, no need for a central server although I would highly recommend one especially if you have multiple developers.
- Good workflow. The index acts almost as a staging area, so I have three statuses: file modified, file added to index (or staging area), and finally file committed. Much better for my workflow.
- Powerful command line utilities. They allow you to interface with your repositories in about every way you can think of.
- Integration into capistrano. You can configure git to use remote caching. Remote caching will keep a local git repo on the server and simply run a fetch from that rather than an entire clone. This means it will only fetch the changes, not everything.
- Friendly community. Head on over to #git on Freenode's IRC server.
- Branching rocks. It couldn't be easier, I can switch from one branch to another with one command. If I don't like the new branch, fine, I just delete it.
Cons
- Poorly documented. Subversion had a nice HTML/PDF book that you could download and it contained everything you needed to know about Subversion. I'm sure it will come in time, but it would be great to have something similar for Git. Until then it's a lot of searching and reading blogs.
- Lack of frontends. There are a few frontends for a variety of operating systems, but nothing I was overly impressed with. In time, I'm sure this is a gap that will be filled.

