Using Git with Subversion

I had the unfortunate experience of having to use Subversion again after using Git for a long time. It is amazing how fast I can forget. After renaming a directory at the prompt, and the agony that goes with it, I decided to switch back to Git.

$ mv requester sampler
# svn agony after renaming a directory
$ svn st
?       sampler
!       requester
!       requester.rb
A       sampler.rb

The tool to use when using Git with Subversion is, of course, git svn.

git svn works very well as long as you remember that Subversion is not Git. It does not handle merging well, and it will bite you if you don’t respect that. So what does this actually mean? It means:

Always keep the Git master in sync with Subversion

To do this you have two commands you can use.

# Rebases the commits from the upstream Subversion server with your local master.
$ git svn rebase

You should only git svn rebase in your Git master, and you should ALWAYS do it before you git svn dcommit anything to the subversion repository. git svn rebase keeps the upstream subversion in sync with your local master by pulling down the changes, kind of like svn update.

# Commits the changes you have in your local master to the upstream Subversion server.
$ git svn dcommit

When you have changes ready to commit, you commit them to subversion with git svn dcommit. You should ALWAYS git svn rebase before you do the update, or it will fail.

That’s it! As long as you follow these two simple rules, your life with git svn will be easy. If you forget to follow them, you will be bitten. When you get bitten, the cool thing about Git is that even if you screw up, it is always possible to sort it out.

It that was all there was to it, there would be no reason to use Git instead of Subversion. Git really shines when it comes to branching and merging. You may create as many local branches as you like with git branch branch_name or git checkout -b branch_name. You can hack around in these local branches as much as you want and merge them together. But, before you merge them into the master branch, you must rebase with master! Not merge, rebase! Rebase means replay the commits on top of the named branch. It creates new commits, the same content, but with a different SHA-1.

# Example session

(master)$ git svn rebase
(master)$ git checkout -b dev
hack, hack, hack, ...
(dev)$ git commit -am 'Commit the changes'

(dev)$ git checkout master
(master)$ git checkout -b bugfix
hack, hack, hack, ..., done

(bugfix)$ git checkout master
(master)$ git svn rebase
(master)$ git checkout bugfix
$ git rebase master
(bugfix)$ git checkout master
(master)$ git merge --ff-only bugfix # --ff-only only fast-forwards, merges that don't need to merge. 
(master)$ git svn dcommit
(master)$ git branch -D bugfix # delete the branch it is not needed anymore

(master)$ git checkout dev
hack, hack, hack, ..., done

(dev)$ git checkout master
(master)$ git svn rebase
(master)$ git checkout dev
(dev)$ git rebase master
(dev)$ git checkout master
(master)$ git merge --ff-only dev # --ff-only only fast-forwards, merges that don't need to merge.
(master)$ git svn dcommit

Another thing to be aware of is that git svn dcommit creates an extra commit, so even if you haven’t changed anything in the master you need to rebase the local branch with the master. This is only needed if you don’t delete the branches after you are done with a commit.

In the example above, I ended with a git svn dcommit and I didn’t remove the dev branch.

(master)$ git svn dcommit # from above
(master)$ git co dev
(dev)$ git rebase master # rebases the extra commit created by git svn dcommit

If you forget to rebase or something else happens that hinders a clean merge into the master. You can always back out of it with git reset --hard.

(master)$ git svn dcommit
... failed miserably, because I failed to git svn rebase, bollocks!
(aa..88dd|MERGING)$ git reset --hard
(master)$ git svn rebase
(master)$ git svn dcommit # Nice and clean commit

To get started you need to clone a subversion repository.

$ git svn clone http://svn.example.com/project/trunk
$ cd trunk
(master)$ git ...

Now, is a good time to start using Git. Get yourself anything by Scott Chacon, such as the book or the screencasts.

This Post Has 8 Comments

  1. Great to see more introductions to git, especially for subversion users. Git-svn is a true life-saver these days :)

    I’d suggest you add –stdlayout to your git svn clone, it will setup remotes for all branches and tags as well as connect remote trunk to your master (no cd trunk needed). If the repo doesn’t follow the default trunk/tags/branches folders you can set that manually as well.

    Also the Pro GIT book is available for free online at http://progit.org/

    Regards
    /Leo

  2. I have been using bazaar on svn repositories, works great. A few weeks ago I intended to try GIT on SVN but after 5 hours on the initial checkout it had fetched ~80% of the revisions (Bazaar took ~45 minutes to finish). Maybe a windows only problem?

  3. Hey I was just wonder if there was a way that the project could still have some reference to the branches and their structure. Because doing it this way, when you run gitx it is a completly flat structure. I know this is a little trivial in the whole scheme of things but it was nice to see how the branches interacted with each other and where they were merged. ?

    Cheers

  4. Another trick that you may find useful is to automatically create a .gitignore file based on the corresponding svn:ignore properties:

    git svn show-ignore > .gitignore

    and then wait a while…

  5. –ff merely *allows* fast-forwards (which is the current default anyway. –ff-only needs to be specified to allow *only* fast-forwards.

    Otherwise an excellent guide. Thanks!

  6. After being svn-free for over 4 years, I have to collaborate with someone within svn. There are some files I want to check into git (mainly .gitignore and .gitattributes) but keep out of svn. It would seem that I want to rebase the opposite of the way the manual and all tutorials suggest in this case — I want all future svn commits rebased on top of this particular git commit. I’ve had a heck of a time doing this without messing around inside $GIT_DIR (finding hashes with grep and replacing them in an editor, which is probably not the right thing to do). Is there a correct way to do this using git commands?

    It does seem to work now, but I know I’m going to need to do it again later, or maybe even amend that private git commit later, so this will be a problem again at some point…

Leave a Reply

Close Menu