Ben Ahrens

Using Git With Subversion (Git and SVN Workflow)

13 Dec 2012

I recently started a new job where the team is using Subversion as its VCS. Coming from a previous job that used Mercurial, I found myself missing a DVCS. The main reason that I miss a DVCS really comes down to how I like write code. My preferred workflow is to constantly work in small iterations, constantly reshaping, manipulating code, until I’m happy with the final implementation. This is often coupled with committing changes to my local repository at various “milestones” along the way, so that I have snapshot of my work, and so that I can move back to a previous “state”, if I ended up exploring a path that didn’t turn out as expected. This kind of workflow really can’t be done in Subversion since every commit is to the central repository. This is where Git and Mercurial shine. I didn’t want to give up my current coding style, and decided to learn how to use a DVCS with Subversion. I chose Git because it seemed to have better support and community information on how to use it with Subversion.

I’m not going to cover the specifics on how to use Git with Subversion. There is already great information available that covers the basics of how to integrate Git and Subversion. Probably the best information for that can be found with the free online version of ProGit. For this blog post, I would like to instead focus on what I found to be a good workflow when using Git with Subversion. I found the information on the Internet about how to use local branches with Git and Subversion to be a little unclear. With some trail and error (and some reading / research) I found a workflow that seems to minimize my pain when using Git local branches with Subversion.

First step is to clone an existing Subversion repository

If you have an existing Subversion repository that follows the standard trunk, tags, branches, convention, you can issue the following command:

git svn clone path_to_svn_repository -T trunk -b branches -t tags

Or the following is shortcut for the above command:

git svn clone path_to_svn_repository -s

Otherwise just clone without any additional flags, like so:

git svn clone path_to_svn_repository

An important thing to note here…this can step can take a VERY long time. If you were using Git for everything, the cloning operation is typically pretty fast. What makes this process slow with Subversion is the fact that in addition to pulling down all the changes, it has to commit them to your local git repository (as opposed to just coping the repository that’s on the server). Depending on the size of your repository this can take hours, even days! Make sure you give yourself enough time let this process run.

Typical Workflow

  1. Update master branch to be in sync with central Subversion repository
  2. Create a new local branch for a small piece of work
  3. Checkout the new local branch
  4. Make some changes and commit them
  5. Rebase master onto the new local branch
  6. Checkout the master branch
  7. Perform a fast-forward merge with your new local branch
  8. Update master to be in sync with central Subversion repository (technically this is optional when using git with Subversion)
  9. Commit master branch to central subversion repository
  10. Delete your new local branch (you may get a warning that this hasn’t merged with master because of svn rebasing - this is ok, just force the delete)

Step 1 - Syncing with Subversion

Assuming you’re on your master branch (you can verify with git branch), perform the following:

git svn rebase

Step 2 - Creating a local branch

Now create yourself a new local branch for working on a feature:

git branch new_awesome_feature_branch

This command creates a local branch that will not exist anywhere else but your local repository unless you issue the appropriate git commands to push this branch somewhere else.

Step 3 - Checkout local branch

You now have a local branch called new_awesome_feature_branch. In order to do work on this branch, you first have to checkout the branch with the following command:

git checkout new_awesome_feature_branch

Step 4 - Make some changes, and commit them

You are now ready to start implementing your “new awesome feature”. The typical git workflow at this point would be to make some changes, and add them to your staging area with the git add command. Once you have your changes staged, commit them to your current branch with:

git commit -m "this is best feature ever implemented"

Step 5 - Rebase local branch onto master

Now comes one of the most important steps, which is integrating your changes with master, so that you can commit them to the central Subversion repository. All the best practices that I found when using git with Subversion, suggested to keep your commit history as linear as possible. Rather than immediately merging your branch back into master, rebase first. To do this, issue the following command:

git rebase master

If you haven’t performed a git svn rebase on the master branch, or rebased other local branches onto your master since you did work on your new branch, the rebasing step will be straightforward and should happen without any merge conflicts. If changes on master have occurred since you branched, then it is possible that you may have to deal with some merge conflicts. I won’t cover resolving merges here, instead I recommend checking out git documentation or reading ProGit.

Step 6 - Checkout master branch

Checkout your master branch:

git checkout master

Step 7 - Fast-Forward Merge

Now for merging your changes back into master. Because you performed a rebase of the master branch onto your feature branch, this next thing to occur will be what is referred to as a “fast-forward merge”. I won’t cover the technical details of this, essentially it is a simple merge process that advances your master branch forward to last commit you made on the new_awesome_feature_branch. Perform the merge with this command:

git merge new_awesome_feature_branch

Step 8 - Sync with Subversion again (optional)

You are now ready to commit your changes back to the central Subversion repository. One thing to note is that if you look at your commit history for the master branch, everything should be very linear and you should not even notice that changes were performed on another branch. It should look as if you performed the changes on the master branch.

If you want to be very thorough before commit your changes, you could get the master branch in sync with the Subversion repository before committing. This is not necessary, because the next step should take care of getting you sync if it’s required. If you remember from earlier, the command for syncing with Subversion is:

git svn rebase

Step 9 - Commit to Subversion

We are getting very close to being done. Let’s push all the commits you have on master, up to your Subversion repository. To do that, issue this command:

git svn dcommit

If you’re interested in what happens during this step, I’ll give you a little background. Because Git and Subversion are two completely different types of version control systems, this command you just issued has to do some special things. One of these is to commit your changes to the Subversion repository and also “rewrite” your commits within your local Git repository while also adding the Subversion revision id so that it can track commits. If you happened to look at the SHA-1 hashes for your git commits before and after running this last command, you’d noticed that they are different because of rewriting.

Step 10 - Delete your local branch

Now that you’re done implementing your new awesome feature, it is safe to delete the new_awesome_feature_branch. I recommend doing this immediately. Git branches are so lightweight and easy to create, that there is no reason to keep this local branch around. I typically create myself a new branch every time I do a small bit of related work. Because of the special things that happen with the Git and Subversion integration, you may get a warning when deleting your branch, stating that it may not have been merged back into master. This has to do with “rewriting” that occurred when committing to Subversion. This is easy to overcome and can be overridden by issuing this command:

git branch -D new_awesome_feature_branch

The capital D is important here. This forces the delete to happen.

Summary

This may have seemed like a lot of information and steps, however, in practice, this workflow is pretty easy. Here’s a quick recap of the steps from a different point of view, the commands you will issue with Git:

git svn rebase
git branch new_awesome_feature_branch
git checkout new_awesome_feature_branch
git commit -m "this is best feature ever implemented"
git rebase master
git checkout master
git merge new_awesome_feature_branch
git svn rebase
git svn dcommit
git branch -D new_awesome_feature_branch

It took me a little while to figure out how to best use Git local branches with Subversion. I hope this information is helpful to others that want to use Git with Subversion, as well as save you some time.

comments powered by Disqus