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
couldn't stop git-svn
from trying to commit Git metadata back into my
Subversion repository...
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-svn
ignored (but that your app might need).Converts any
svn:ignore
Subversion properties into entries in the.git/info/exclude
file.Checks for Rails plugins installed via
svn:externals
and clones them into their own git repositories. Each external plugin is then symlinked intovendor/plugins
.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.
Installation
You need 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
Installing git-me-up
The source is hosted on GitHub. You can either download
the tar ball or pull
the source with git
:
$ git clone git://github.com/gma/git-me-up.git
The Makefile
will install it into /usr/local/bin
:
$ cd git-me-up
$ sudo make install
Using git-me-up
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 1.5.4.4) 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
repo.
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 BRANCH
environment variable before you run git-me-up
.
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
your 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
svn:externals
called site_theme
. I usually use
piston or
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 svn:externals
property.
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 git-svn
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
ignore it:
$ 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.