Easy git-svn for Rails (or "git-me-up")
I've been trying to persuade
git-svn to work properly with Rails
plugins that are installed via
svn:externals. Whilst working out how
to do it I stumbled across several great articles, but I couldn't get
any of the solutions presented to work perfectly. Samuel Tesla's
article is especially informative, but for a long time I
git-svn from trying to commit Git metadata back into my
After a bit of head scratching I got it sorted, and wrapped the process up in a little utility called git-me-up.
What does git-me-up do?
In a nutshell, it:
Clones the latest version of the source in your Subversion repository into a new local Git repository. It doesn't import any history for performance reasons.
Recreates empty directories that
git-svnignored (but that your app might need).
svn:ignoreSubversion properties into entries in the
Checks for Rails plugins installed via
svn:externalsand clones them into their own git repositories. Each external plugin is then symlinked into
Creates a local working branch.
In other words, it takes all the hard work out of creating a local Git repository that points at your Subversion repo.
git-svn installed first. On a Mac, install
MacPorts and then type:
$ sudo port install -u git-core +svn +bash_completion
When the package has finished building run these commands to benefit from bash completion:
$ cp /opt/local/etc/bash_completion.d/git ~/.git-bash-completion.sh $ echo '[ -f ~/.git-bash-completion.sh ] && . ~/.git-bash-completion.sh' \ >> ~/.bashrc $ . ~/.bashrc
On Ubuntu Linux:
$ sudo apt-get install git-svn
$ git clone git://github.com/gma/git-me-up.git
Makefile will install it into
$ cd git-me-up $ sudo make install
This is pretty easy. Let's start with an empty directory so you can see what gets created:
$ pwd /home/graham/test $ git-me-up https://myhost.com/path/to/myproject/trunk myproject git-me-up: finding latest revision of https://myhost.com/path/to/myproject/trunk git-me-up: creating git repository in /home/graham/test/myproject git-me-up: making empty directory: app/views/comments git-me-up: making empty directory: app/views/pages git-me-up: making empty directory: components git-me-up: making empty directory: log git-me-up: making empty directory: test/mocks/development git-me-up: making empty directory: test/mocks/test git-me-up: making empty directory: tmp/cache git-me-up: making empty directory: tmp/pids git-me-up: making empty directory: tmp/sessions git-me-up: making empty directory: tmp/sockets git-me-up: setting ignored files from subversion (this can take a while) git-me-up: checking for plugins installed with svn:externals git-me-up: finding latest revision of https://myhost.com/path/to/plugin/trunk git-me-up: creating git repository in /home/graham/test/myproject/../plugins/site_theme git-me-up: setting ignored files from subversion (this can take a while) git-me-up: symlinking site_theme into myproject/vendor/plugins git-me-up: creating 'work' branch on /home/graham/test/myproject Switched to a new branch "work" $ ls myproject/ plugins/
If you're running on a Mac you may see this error message part way through the output:
No such file or directory: PROPFIND request failed on '/app': Could not open the requested SVN filesystem at /opt/local/bin/git-svn line 1872
It appears as though the
git svn show-ignore command (using the
current version of git from MacPorts--version 18.104.22.168) doesn't work on
my Mac. If you see this too don't worry about it; it will only mean that
Git doesn't know which files you've chosen to ignore in your Subversion
Now we're ready to use our new Git repo. Note that we've got a work branch by default:
$ cd myproject $ git branch master * work
If you want to call the branch something else, set the
environment variable before you run
Samuel recommends a good work flow for working with Subversion (skip down to "Step Five: Using this thing"). When you want to update your working branch from subversion, try this:
$ git checkout master # switches to local master branch $ git svn rebase # pulls recent changes from subversion $ git checkout work # switches to local work branch $ git rebase master # pulls recent changes to master into work
There's a good description of why rebase is so great on James Bowes's blog.
When you want to commit changes back into subversion:
$ git checkout work # you were probably there already ... run your tests ... $ git commit -a -m "Blah blah blah..." $ git checkout master $ git merge work $ git svn rebase # checks for fresh changes in subversion ... run tests again if rebase pulled any changes ... $ git svn dcommit
Make sure that you use
git svn rebase to pull upstream changes into
master branch before you check in.
What git-me-up does with svn:externals plugins
The Rails app that I cloned above has a plugin that is installed via
site_theme. I usually use
braid to install Rails plugins, but
when the plugin is stored on my own servers I've always preferred to
pull in the source directly by setting the
I initially tried to convert
svn:externals into Git submodules, but it
didn't work well. No matter what I tried, I couldn't persuade
to ignore the submodule when committing back to Subversion. It turns out
that it's easy to achieve with a symbolic link.
git-me-up cloned the
site-theme plugin into a Git repository, then
made it available with a symbolic link:
$ ls myproject/ plugins/ $ ls plugins/ site_theme/ $ ls -l myproject/vendor/plugins/ | grep site_theme lrwxrwxrwx 1 graham development 32 Apr 24 18:41 site_theme -> ../../../plugins/site_theme/
To avoid checking the symlink into Subversion,
git-me-up told Git to
$ grep site_theme myproject/.git/info/exclude /vendor/plugins/site_theme
The result is a trouble free alternative to submodules that allows you
to integrate Git with
svn:externals in your Subversion repo.
I love feedback and questions — please get in touch on Twitter or leave a comment.