7 Sep 2011 fallenlord   » (Journeyer)

Working with git and Subversion

One of the daily challenges I have in my job as the build configuration engineer in ITRS is to work with the existing Subversion repository. With the memories of surviving three years of pain, misery and suffering of merges with its precursor CVS and enduring equally horrific merges with Subversion in my former jobs, I simply cannot bear using Subversion again.

Enter git. Git for a very long time has been used as a Subversion client, often subversively in the early days. Thanks to the git-svn conduit, it has become very routine for many people to use git as a Subversion client while maintaining git's advantages when it comes to merges. Armed with this knowledge, I now have an incentive to level up my git prowess to keep the use of Subversion at bay. 

So far here's a listing of how I have been using git within a Subversion workflow.

Importing modules from Subversion

As git requires the entire history of the repository, you cannot use an existing working copy of Subversion modules that you have checked out using the Subversion client. You will need to perform a clone of the Subversion module into a local git repository as follows:
$ git svn init svn://svn.example.com/awesome_product/trunk
$ git svn fetch -r15110
This creates a git repository contained in the trunk directory that contains the said module/folder in Subversion by checking out from the remote repository at revision 15110, and assigns a unique git-svn-commit ID per commit in Subversion.
Here is a more-thorough, but slower to resolve one-liner that will clone the repository:
$ git svn clone svn://svn.example.com/awesome_product/trunk
Unfortunately, pulling modules from Subversion branches has also the same cost as pulling from any other branches (e.g. trunk) and that you will still need to do the cloning of the repository as prevously described.

Pulling the latest stuff from Subversion

As Subversion only allows a linear history (as opposed to git that can allows branched history tracking), pushing to the remote Subversion server requires pulling first the latest code present in Subversion then putting your changes on top of it. This you can do via the rebase operation, like this:
git svn rebase
NOTE: It is preferred that you perform the rebase operation on the master branch if you are performing all feature development in their distinct branches, like how normal git users would do.

Pushing code to the Subversion repository

Pushing your commits to the Subversion server can be done by issuing a dcommit:
git svn dcommit
This pushes all your commits in one pass: making a Subversion commit for each commit performed in git, and rewrites each commit in git to add a git-svn-commit ID.

Working with Subversion Branches

Oftentimes there'll be a need to merge your work from the trunk Subversion branch (which would usually be the master branch in your git repository) to another feature branch in Subversion (e.g. RELEASE-201109A) Git makes it easy to merge commits with the benefit of merge tracking and the ability to cherry-pick commits that you wish to port.
There is no need to to create a new checkout of the codebase: instead you'll need to fetch all the objects from the release branch. If you open up the .git/config file in your git repository, you may see something like this:
  [svn-remote "svn"]    
  url = svn+ssh://svn://svn.example.com/awesome_product/trunk
  fetch = :refs/remotes/git-svn
You will need to declare a new remote branch similar to your existing remote repository, but this time, referencing the release branch:
  [svn-remote "release201109A"]    
  url = svn+ssh://svn://svn.example.com/awesome_product/RELEASE-201109A
  fetch = :refs/remotes/git-svn-release201109A
To pull all objects from that remote branch from revisions 49110 to the latest, say 15110, for example, you can do:
$ git-svn fetch release201109A -r 49110:15110
If you will check the list of all local and remote branches (via git branch -a), you will see a new remote branch named git-svn-release201109A. To create a local branch that would contain the contents of the remote branch, you will have to check it out just like a remote git branch:
$ git checkout git-svn-release201109A
$ git checkout -b release201109A
Now we've got a new release201109A branch that can be used for git svn rebase and git svn dcommit just as your old master could (assuming you have enough privileges). From here, cherry-pick the patches you want to port from your master branch to your own copy of the feature branch, and if you have enough permissions, commit it to the Subversion repository via git svn dcommit.

Cherry-picking commits from one branch to another

Using the git-cherry-pick operation, you can port changes from one branch and port these to another branch. An example follows:
You were asked to merge commits that correspond to Bug 666 and port these to the branch release201109A which represents the Subversion branch RELEASE-201109A. Using git log, you found the following corresponding commits: 5e2a80cbb2d452d515a59d5ae7498df615a24d5f and 52ac80e1c627ae86ebd81581ce25a2e32a142241.
To proceed in merging these two commits from your git repository's master branch to the release201109A branch, just do the following:
$ git checkout release201109A
$ git cherry-pick 5e2a80cbb2d452d515a59d5ae7498df615a24d5f 52ac80e1c627ae86ebd81581ce25a2e32a142241
The git-cherry-pick operation merges those changes to the release201109A branch even if you're offline (which you can't do with Subversion). Then do a git svn dcommit after the merges are done.

Re-enable Subversion tracking

Normally what you or your team members will do is to clone from a git repository from someone else who has performed the initial checkout via git svn clone. Your git repository then wouldn't have the references to the Subversion repository as compared to guy who made the original checkout from the Subversion service. That also means that your git repository cannot push to the Subversion server, nor can query the state of the Subversion service like the source git repository.

In order to let your git repository work again with the origin Subversion repository, you will have to add tracking to the Subversion server:

[svn-remote "svn"]    
url = svn+ssh://svn://svn.example.com/awesome_product/trunk 
fetch = :refs/remotes/git-svn

Once done, you will then have to let git update the references mapping the existing objects in your git repository to the Subversion repository by issuing:

$ git update-ref refs/remotes/git-svn refs/remotes/origin/master
This command will take quite a few minutes to resolve but afterwards you can already perform the basic git svn  set of commands.

Syndicated 2011-09-07 18:57:00 (Updated 2012-03-03 02:50:38) from Paolo Alexis Falcone

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!