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 svn
and git
commands—like me. For us, it’s far easier to simply use git as a Subversion client and never have to call svn
directly.
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 -Rremote_name
or --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 update
would do), usegit 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.
This post is pure gold. I keep going back to refer to it each time there’s an SVN repo I want to abstract into git – and there are a lot of SVN repos. Thanks!
I’m glad this is helpful for you, Johnny. :D Thanks for being one of the rare good people who come back to say thank you.
What do ya do if the repo uses svn:externals? It seems that there is not a nice workaround available.
BerT, for a SVN repository that uses
svn:externals
, you can use this git-svn-clone-externals tool. I’ve used it in a number of projects that are hosted by a central Subversion repository with multiplesvn:externals
definitions in them, and this tool is remarkably straight-forward. Good luck.Meitar
In the interest of being…
one of the rare good people who come back to say thank you.
…thanks!!!
BTW, checking your code for changes:
git svn status
doesn’t exist for obvious reasons. Aftergit svn clone
you have a git repository on your disk, thereforegit status
is just fine for checking.I think git-svn does not synchronize it’s refs or remote branch information across push/pull to, say, a github repo. Assuming this is true, how do I replicate the same git-svn state from machine A to machine B? I want to work on my github repo and git svn rebase changes from upstream SVN from any machine I’m working on. Is this possible? Thanks!
I would also like to try the way / workflow James Dunne is suggesting.
Great info, but the search term highlighting is incredibly distracting.
Am I missing the button/link to turn it off?
Hi, Meitar – thanks for this. It’s one of the most concise and useful versions of the information. One note: I used -Rorigin on the original clone so that I could use “git push origin master”, etc., just as if I were working with Github.
Thanks a bunch for this post. This answered some of the questions I needed for using Git with SVN repos. Awesome!
It is 2011 and this post is still awesome. Thanks!
Very handy.
Thanks a lot for the clear description.
I am trying to use gitsvn to maintain a mirror of an svn repository on github. It works fine if I do it all from one computer, but as soon as I move to a different computer and want to update the mirror, I have trouble. I’ve detailed the problem here:
http://stackoverflow.com/questions/10886784/updating-a-git-mirror-of-an-svn-repository
Could you clarify this use case in your tutorial?
Thanks!
David
This entry is still useful.
Thanks!!
Thank you for this great post!
I would certainly like to follow the good examples of Johnny and Carlus – thanks a lot for this article, Meitar. I would also like to restart the question posted by James Dunne and Tuomo two years ago.
Any opinions anyone?
It’s 2014 and this is still the best guide around to git svn.
Thank you got sharing this with us!
I’ve been doing this for several months now and this is probably one of the better write ups I’ve found. I’ve found that it’s important to git svn rebase before every git svn dcommit which could just be a peculiarity of my environment but I have the least heartache as a result. I’ve also been able to push the code out to github so this method has been essential in my plan to migrate my team off of SVN altogether.
Thanks for taking the time to document this.