Subversion is a lot better than no version control system at all, but for those accustomed to distributed version control systems like Git or Mercurial, it can be pretty painful to lose features like cheap and flexible branching, intelligent merging and rebasing, and the snappy operations of Git when the need arises to work on code in a Subversion repository, perhaps for a legacy project or for an organisation with established repositories.
Fortunately, there’s an excellent compromise available in using the git-svn
wrapper, which allows you to treat a Git repository as a working copy of a
Subversion repository. This works transparently, meaning that others using the
regular svn
client on your team won’t have any difficulty working with your
commits or branches, but your private Git workflow can meanwhile be as simple
or complex as you like.
Installing
If you’re on Debian or Ubuntu, you can install the git-svn
wrapper with:
# apt-get install git-svn
If you’re installing Git from source, it’s included in the default installation.
Cloning (Checkout)
To check out a copy of the trunk of the repository, you can clone it directly:
$ git svn clone svn://server.network/project/trunk project
This will provide you with a working copy of the repository’s trunk in the form
of a Git repository, complete with a git log
history imported from the
Subversion commits. If you want to include tracking for the repository’s
branches and tags as well, you can specify the paths to them on the command
line:
$ git svn clone svn://server.network/project project \
--trunk trunk --branches branches --tags tags
Typing git branch
and git tag
in the resulting repository will then show
these as available branches and tags, as if they’d been created in Git. If the
Subversion repository has a standard layout of folders, with branches
,
tags
, and trunk
, you can pass the --stdlayout
or -s
as a shortcut for
the above:
$ git svn clone svn://server.network/project project --stdlayout
Fetching (Updating)
To pull the most recent changes from the Subversion repository into your Git “working copy”, use:
$ git svn fetch
If you have local changes in your repository that have not yet been committed,
you may be prompted to temporarily cache them while you run the fetch
operation. Git’s stash
function works well for this:
$ git stash
$ git svn fetch
$ git stash apply
Committing
You can commit to your Git repository as normal with git commit
. When you’re
ready to send these commits to the Subversion repository, you can do so with:
$ git svn dcommit
This will also note information about the Subversion commit in the output of
git log
.
Branching
If you want to create a new branch in Git that tracks a similarly created branch in the Subversion repository, you can do this:
$ git svn branch experimental
It’s useful to note that if you don’t have any need to add the branch you’re
creating to the Subversion repository, you can just use the usual git branch
to keep a branch restricted to your Git repository. You should only need the
git svn branch
facility if others might need to use that branch before you
merge it.
Merging
While it may be possible to conduct merges within the Subversion repository
from the git-svn
client, I think this particular task is probably best done
using the usual svn merge
tool. If you’re up to reading how this works in
man git-svn
, you should go for it, but I don’t think that getting a handle on
the complexity of the rules for how these merges are run is really worth the
effort in most cases, and given how brittle Subversion can be it’s likely not
worth the risk of breaking things.
Properties
Aside from the above recommendation about using the native svn merge
to
conduct merges of Subversion branches, another limitation of the git-svn
client is it ignores Subversion properties like the very useful svn:ignore
,
and furthermore doesn’t provide any way to set them. As a result, after the
clone Git won’t know which file patterns a traditional Subversion working copy
would be set up to ignore. You can emulate this by writing the properties file
to a .gitignore
or the .git/info/exclude
files:
$ git svn show-ignore >> .gitignore
$ git svn show-ignore >> .git/info/exclude
Empty directories
Finally, Subversion and Git differ in how they treat empty directories. As a
result, you may create an empty directory in a Git repository with intent to
commit it into the Subversion repository, and find yourself unable to do so.
The workaround here is to place some sort of file into the directory and commit
that; a README
file explaining the directory’s purpose seems to be a sensible
choice.
These limitations, among others, show that the mapping from Subversion’s
functionality to Git’s isn’t perfect, but for those who find working with
Subversion a bit painful or imprecise, the functionality available in git-svn
can certainly help, until such time as you’re able to convince your
repository’s host to migrate everything to a more modern and capable revision
control system.