January 20, 2009
Inspired by Michael Ivey's recent "My Git Workflow" article, I've decided to write up one of my own. Really this article should be titled "My Git SVN Workflow", because that's how I use it most of the time. At BrowserMedia, we use Subversion (SVN) for version control. I'm not an advanced user of Git by any stretch of the imagination, but here's my experience from switching from SVN to Git.
First off, for small projects that I am doing on my own, I use just straight git, and back it up to my own server as I described in the Quick and Dirty Git Repository article. For stuff that I want to be publicly visible, I push it to github. But for BrowserMedia internal projects, here's what I do.
Assuming it's a rails project, I create the rails app and import it using SVN:
$ rails myproject -d mysql $ cd rails $ svn import -m "Initial Rails 2.2.2 App" https://svn.browsermedia.com/myproject/trunk $ cd .. $ rm -rf myproject $ svn co https://svn.browsermedia.com/myproject/trunk myproject $ svn remove log/* $ svn commit -m "removing all log files from subversion" $ svn propset svn:ignore "*.log" log/ $ svn update log/ $ svn commit -m "Ignoring all files in /log/ ending in .log" $ svn remove tmp/* $ svn propset svn:ignore "*" tmp/ $ svn update tmp/ $ svn commit -m "Ignoring all files in /tmp/" $ cd .. $ rm -rf myproject
Most of this you will recognize from the Rails Wiki article on How To Use Rails With Subversion. So now the project is setup so that anyone can use SVN to work on the project.
One of the cool things about Git is that you can use Git SVN to work with git locally and push your changes to the main SVN repository. I've been using this for months now with no problems. So now let's start working on our project with git:
$ mkdir myproject $ cd myproject $ git svn init -s https://svn.browsermedia.com/myproject
Couple of things to note here is that first you have to make the directory for your project first, then change into the directory and run the git command from there. Also, the URL you use is the directory that contains
trunk (and possibly
tags, if you have created those). The
-s means you are using a standard SVN layout. Once you have done this, you run this command to sync up your local repository with SVN:
$ git svn fetch
In this case, this is a fresh project, so if should go pretty quick, but if you are doing this with an existing larger project, this could take quite some time, because this is not just fetching the latest version of the code, but the entire history of the repository.
To make working with git a little easier, I have some aliases defined to make things go a bit quicker:
alias gs='git status' alias ga='git add .' alias pull='git pull' alias push='git push' alias gc='git commit -m' alias gca='git commit -a -m' alias svnpull='git svn rebase' alias svnpush='git svn dcommit'
And then most of the projects I work on are small teams, so I follow Michael's small team workflow pretty closely:
$ svnpull Hack some stuff $ gs look at what's changed $ git add . $ gca "hacked some stuff" $ svnpush
So the first thing I usually do after
git svn fetch is to add a
.gitignore file to the project that looks something like this:
.DS_Store log/*.log tmp/**/* db/*.sqlite3 db/schema.rb
One nice thing I've observed about using Git over SVN is the way it handles directories of files. Git puts one
.git directory at the top level of your project, which is where it keeps the info about the repository, including the local repository itself. SVN puts a
.svn directory in every directory of your project. This only has the info about the repository, which files you have checked out, etc. There is no local repository of course, the repository is on the server.
A case where this makes things easier is managing external dependencies. Let's say you have a gem that you can't or don't want to install on your server. For most gems I recommend installing them on the server, but in some cases that doesn't always work. So in that case, you do
rake gems:unpack to put the gem into the vendor directory. Now let's say you need to update that gem. Again, this is not the normal case, but if you are creating your own gems for your project, you might want to do this. Here's what you can do with git:
$ cd important_gem $ rake install #installs version 1.0.0 of important_gem locally $ cd ../myproject $ rm -rf vendor/gems/important_gem-1.0.0 $ rake gems:unpack
The case here is that
myproject already has a version of
important_gem-1.0.0 checked in, but you've modified
important_gem-1.0.0, so you want to get those updates into
myproject. You only do this in a situation where you haven't released version 1.0.0 of important_gem yet.
Now if you run
gs (short for
git status), you will see that git has just figured out what happened. Whether files were removed, renamed, added or modifed, git knows. You can just run
git add . and then
gca "updated important_gem" and all is well. No need for SVN externals. With SVN, if you wanted to manage your project like that, you are kind of out of luck. The reason is that when you did
rm -rf vendor/gems/important_gems-1.0.0, all of the
.svn directories got destroyed. After
rake gems:unpack, the files are back, but not the
.svn directories, hence SVN is confused and frankly, I never did figure out what you are supposed to do about this.
There are other cases where this is useful besides working on a gem. For example, with BrowserCMS were are using FCKEditor, which comes in a zip file that you just unpack into
public/fckeditor. To put in a new version of FCKEditor, you can just do:
$ cd public $ rm -rf fckeditor $ mkdir fckeditor $ cd fckeditor $ unzip ~/Downloads/fckeditor-x-y-z.zip $ cd ../.. $ gs $ git add . $ gca "Upgraded to FCKeditor version x.y.z"
There are probably some errors or inefficiencies in this process and if so, let me know in the comments, but I really like working with Git SVN, which allows me to have my cake and eat it too.