I’ve been using git as my favorite version control tool for quite a while now. One of its numerous distinguishing features is an optional component called git-svn, which serves as a bi-directional “bridge” that enables native git repositories to interact with a Subversion repository, performing all the normal operations you would need to use
svn for. In other words, since you can checkout, commit to, and query the logs of Subversion repositories (among other things) using git-svn, git can serve as your all-in-one Subversion client.
One reason why you might use git-svn because your project actually resides in a Subversion repository and other people need to access it using Subversion-only tools. Another might be because you have multiple projects, some that use git and others that use Subversion, and you’re tired of switching between
git commands—like me. For us, it’s far easier to simply use git as a Subversion client and never have to call
As an important aside, please note that I would strongly discourage people who are new to git from learning about it by using git-svn. Although you may think that moving to git from Subversion would be eased by using the git-svn bridge, I really don’t think that’s the case. You’re much, much better off simply using git by itself right off the bat, and you can do this even if your fellow committers are using subversion.
Also, I’m going to assume you’ve already got a Subversion repository set up somewhere.
First, checkout the subversion repository. In Subversion you would do this:
svn checkout http://example.com/path/to/svn/repo
With git-svn, you do this:
git svn clone http://example.com/path/to/svn/repo
This will cause git-svn to create a new directory called
repo, switch to it, initialize a new git repository, configure the Subversion repository at http://example.com/path/to/svn/repo as a remote git branch (confusingly called
git-svn by default, although you can specify your name by passing a
--svn-remote=remote_name option), and then does a checkout.
The output of this command will be a little awkward. Here’s a sample from one my repositories:
r14 = dbd7266f328ef2ad061ea4532f39ce7cebaba0c5 (git-svn) M trunk/Chapter 6/Chapter 6.doc M trunk/Chapter 6/code examples/6.1.html A trunk/Chapter 6/code examples/6.2.html r15 = 4cca08341ab0600069cece77ce67afc449caca68 (git-svn) M trunk/Chapter 6/Chapter 6.doc A trunk/Chapter 6/code examples/print.css A trunk/Chapter 6/code examples/screen.css M trunk/Chapter 6/code examples/6.1.html M trunk/Chapter 6/code examples/6.2.html r16 = 7b2f3e0ccfd79be61b527b6ba325f8689475dc01 (git-svn) M trunk/Chapter 5/Chapter 5.doc r17 = a319764855361d92bb6e006cfd18a51319046cae (git-svn) M trunk/Chapter 5/Chapter 5.doc r18 = 4cd5cb43d33b2dd45bd39b9a2b7ea9416f9e3d8f (git-svn) M trunk/Chapter 6/Chapter 6.doc M trunk/Chapter 6/code examples/screen.css M trunk/Chapter 6/code examples/6.1.html
As you can see, git-svn is associating specific Subversion revisions with particular git commit objects. Due to this required mapping, the initial cloning process of a Subversion repository may take some time. This is a good opportunity for your morning coffee break.
When this process is done, you’ll have a typical git repository with a local master branch and one remote branch for the Subversion repository:
Perseus:repo maymay$ git branch * master Perseus:repo maymay$ git branch -r git-svn
You can now treat the Subversion repository as though it were a remote branch of sorts. Say you’ve done a bunch of work and, as you typically do with git, you commit this work to your topic branch.
Perseus:repo maymay$ git checkout -b awesome-feature Switched to a new branch "awesome-feature" Perseus:repo maymay$ vim awesome-feature-stylesheet.css Perseus:repo maymay$ git add awesome-feature-stylesheet.css Perseus:repo maymay$ git commit -m "Now I'm perty." Created commit 07ee832: Now I'm perty. 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 awesome-feature-stylesheet.css
Right now your changes are still in the topic branch (called
awesome-feature in the above example). To get them to Subversion, you merely need to say
git svn dcommit:
Perseus:repo maymay$ git svn dcommit Committing to http://example.com/path/to/svn/repo ...
Note that pesky extra “d” in the command. This is the equivalent of Subversion’s
svn commit, but the commit message used is the one from the previous command, which in this case was
git commit -m "Now I'm perty.". Also interesting to note here is that because Subversion doesn’t understand git branches, any change on any branch can be “pushed” to Subversion at any time using
git svn dcommit—the git commits don’t have to be on any specific branch, since all git-svn does is map a git commit object to a Subversion revision and vice versa.
Similarly, you can at any time run the equivalent of
svn update to get the latest changes from the Subversion repository into your Subversion branch.
- To do this, without affecting your working tree—that is, to only fetch the latest changes but not write them to the filesystem, just to the git-svn metadata area and the remote git branch—use
git svn fetch. To apply these changes to your local branch, you simply merge:
git checkout master; git merge git-svn.
- If you do want to write out the changes to the filesystem (as
svn updatewould do), use
git svn rebase, which automatically linearizes your local git commit history after the commit history of the incoming Subversion changesets. Very slick.
If your fetching/rebasing causes a conflict, you’ll be notified and will have to resolve it as per usual. If your “pushes” to the svn repo causes a Subversion conflict, you’ll be notified and you should again edit the appropriate files to resolve it, but this time make sure you run a
git svn rebase before you try
dcommit-ing again (since, remember, Subversion can only handle linear commit history).
As always, saying
man git-svn or
git help svn to your shell will give you all the other details. Among these, the most likely you’ll probably want to learn about is how to track multiple Subversion branches as normal git branches.