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.
If you have an existing Subversion repository that follows the standard trunk, tags, branches, convention, you can issue the following command:
Or the following is shortcut for the above command:
Otherwise just clone without any additional flags, like so:
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.
Assuming you’re on your master branch (you can verify with git branch), perform the following:
Now create yourself a new local branch for working on a feature:
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.
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:
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:
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:
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.
Checkout your master branch:
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:
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:
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:
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.
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:
The capital D is important here. This forces the delete to happen.
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:
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.