Choosing a version control system isn’t an easy task, there are many challenges like workflow within your team’s style, ability to work with your build process and release schedules, developer’s style and preference and most importantly the teams comfortability with the tools. Often this choice is made with the least common denominator in mind.
On my current assignment, the version control question was already answered: SVN was the choice. I have been using mercurial and git for a while on some personal projects collaborating with friends. Professionally, I was at a startup years ago that moved from subversion to Mercurial for agilities sake. I was hooked. I could work with other developers on features that would have blocked the rest of the development team if we had been checking that broken code into trunk.
I had seen and read about people using distributed version control systems(like git or hg) as a superclient for centralized ones. With svn and git this works by maintaining a git repository on top of your svn checkout, allowing you to control and shelve changes much more easily than a set of patches. After trying this method a few times I wasn’t able to find a rhythm that worked for me. It almost always ended with inconsistencies in the .svn folders littered everywhere and files getting out of sync beyond svn cleanup‘s reach.
When I heard that you could use git as a svn client, I was intrigued and gave it a fair chance. This is the workflow that I’ve found to be most harmonious with me and my coworkers didn’t even need to know I was using git this way
Step One: Clone the repository.
git svn clone -s <url>
The -s flag tells the clone to use the standard trunk/branches/tags convention. This will clone the entire repository *Be careful* this may take a while depending on the number of revisions, branches and tags.
After cloning the subversion repository, you should be able to see the local and remote branches by doing a git branch -a
Step Two: Make changes and commit back to SVN.
Now you are free to make changes to your code. When you feel like you are ready to commit them to your local git repository, then go ahead and commit those changes locally as with any other git repository. (
git add , and
git commit , etc.)
Remember that when you commit code that you are doing it on a local branch and it hasn’t been pushed back to the central SVN server yet. When you are ready to commit your code to SVN, you would simply use the
git svn dcommit command. This command will fetch the latest changes, replay your code on top of it and commit your checkins into the central SVN server (See the docs for more).
Changes = new local branch (they are cheap!!)
I typically like to create a new local branch for each new feature or unit of work. So for a feature, FeatureA, I’d execute
git checkout -b FeatureA. This will create a new local branch called FeatureA. You are now ready to work on your feature and commit your code to the local branch.
If I want to update to the latest version of your trunk (think: svn update), I would use the
git svn rebase command. This will basically replay my current changes on top of the latest code from svn. If there are any merge conflicts when you rebase, then fire up your merge tool (
git mergetoool) and fix it. There is a nice message that tells you that when you are done merging manually that you should execute
git rebase --continue to continue the last rebase. I’ve only run into this a few times when there was heavy development or tab/whitespace wars.
Step Three: Switching contexts is easy — FTW!
Lets face it, we are interrupted all the time. I try to stay focused on my current feature/task but support requests, emergency issues, peers and business folks switching priorities end up causing us to switch contexts frequently. I’ve found that this is one area that git(DVCS) shines the brightest. If I’m in the middle of FeatureA and I’m interrupted I can either commit the unfinished/untested code to the local branch or stash it using the
git stash command. I typically use stash for quick interruptions and save the
amend tactic for more lengthy endeavours.
Once I’ve stashed or committed my code on my FeatureA branch, I’m free to address the interruption. Upon finishing addressing the interruption I can simply go back to my FeatureA branch via
git checkout FeatureA and apply the stashed code by the
git stash apply command.
I like to keep my master branch clean and perfectly in sync with trunk. (trunk = master) in my mind anyway. It doesn’t really matter, ‘master’ is just a convention and can be thrown away if you like – but its handy to have the pristine version of the trunk that your master branch is tracking on your local machine
git svn fetch pulls the changes into the repository.
git svn rebase bases your changes on the latest from the svn (trunk in our example).
There are many other features that are beneficial like tracking different SVN branches, merging between them, cherry picking, interactive rebasing, filtering commits, etc. I’ve been very satisfied with git & git-svn so far.