Archive for the ‘Howto’ Category

Git branches to handle contributor patches

Thursday, December 4th, 2008

If you manage an opensource project, chances are high that external contributors will submit patches to you.

Patches are sometimes quite easy to handle. For example a trivial bugfix, a documentation update or any other small size patch is fast to review. First you read the patch hunks to figure out what it does, and if you like it you try to apply it. If it applies you can test it, and if you still like it, it gets commited to your favorite revision control systems (SVN, Mercurial, Git, …whatever).

There are however some cases where this process is simply too fast. The patch may change significant portions of the source code. It may imply further testing. You may need time to make your mind on whether it should be commited or not. And more simply: once applied you may want to polish / rework it.

For all of those non-extensive cases, you need to put the patch “on hold” for a while, then eventually commit it. Here’s how I have come up to deal with them recently for the IzPack Java installer project.

IzPack uses Subversion as its revision control system, and the almighty Atlassian JIRA issue tracker, kindly hosted at the Codehaus.

When you think about patches in pending state, branching in a revision control system sounds quite appropriate. Sadly, everyone knows (or everyone should know) that Subversion is really bad on branching. Of course it branches fast, but merges are absolutely awful in Subversion, although I hear it has improved a bit recently. Also, you need to make your branches public, which is not always what you want.

By contrast Git makes it easy to branch and merge like crazy once you’ve figured out how Git works and you’ve shooted yourself in the foot once or twice ;-)

Luckily, it has some support for interacting with Subversion repositories, which means that you can work in Git then push changes back to Subversion once you want it.

So here is how it goes for those cases: I create a branch for each pending patch. The naming convention that I use is damn impressive: pending/JIRA-ID. Say that I need to work on IZPACK-163: I create a branch called pending/IZPACK-163.

Next I can git apply patch-file and commit the patch, and later continue with more commits along as I test / rework the patch. Once I am done, I can simply merge it back to my Git master branch, and do a squashed commit to a local branch that mirrors the Subversion trunk/branch that I am targeting.

As far as I am concerned, I have found this workflow to be very efficient and agile. In a nutshell: one patch (identified by its issue id) equals one branch. Can’t be any simpler I think :-)

Oh and I forgot: with Git I can easily and quickly jump from one branch to another. I can also easily put back some new changes from Subversion into the pending patches branches, as Git remembers merges just right.

What do you think about it? Have you used similar / alternative techniques?

Working on IzPack with Git and Subversion

Thursday, October 23rd, 2008

2008/10/27: I have removed my vendor-svn-trunk branches on Github. Indeed, I realized that I have not been using them properly. In the future, I will always consider master branches as the exact copies of Subversion trunks, i.e., new commits in master will appear after Subversion commits (mines or not) have been fetched locally.

These instructions will be helpful to the IzPack developers, but you can certainly adapt them easily for your own project.

The idea is to use Git, a decentralized SCM tool along with Subversion. This is getting quite common to mix both tools: Subversion hosts the “official” source code while developers and external contributors may collaborate with more flexibility in Git.

Git plays nice with Subversion. One can easily “clone” a Subversion repository into a Git one. This is however a lengthy process if your project has accumulated many revisions. I did that once for the IzPack Subversion trunk, then published it to Github.

Someone that has commit access to the project in Subversion can easily use Git as well. Instead of converting the whole repository again, there is a simpler solution. I use that on other machines too when I want to work on IzPack using both Git and Subversion.

First, clone the trunk with only a few of the latest revisions. Unless you really have to dig far in the past, you won’t need much of those revisions anyway ;-)

git svn clone --username=jponge -r 2300:HEAD -T https://svn.codehaus.org/izpack/izpack-src/trunk izpack.git

It is then easy to check that I have a master branch with a special trunk branch that is handled by git-svn:

jponge:izpack.git julien$ git branch -a
* master
  git-svn

I can then easily add my GitHub remote branches. Use a different clone URL in your case: either the read-only one of my GitHub repository, or your very own fork of it.

git remote add origin git@github.com:jponge/izpack.git
git fetch
warning: no common commits
remote: Counting objects: 17652, done.
remote: Compressing objects: 100% (4214/4214), done.
remote: Total 17652 (delta 11961), reused 17611 (delta 11942)
Receiving objects: 100% (17652/17652), 14.49 MiB | 265 KiB/s, done.
Resolving deltas: 100% (11961/11961), done.
From git@github.com:jponge/izpack
 * [new branch]      master     -> origin/master
 * [new branch]      vendor-svn-trunk -> origin/vendor-svn-trunk
 * [new branch]      with-git-submodules -> origin/with-git-submodules
jponge:izpack.git julien$ git branch -a
* master
  origin/master
  origin/vendor-svn-trunk
  origin/with-git-submodules
  trunk

This fetches more data as my GitHub repository contains the whole IzPack history that I had converted a while back from Subversion.

You can see that I work with a few branches:

  • origin/master is… the master Git branch
  • origin/vendor-svn-trunk is where I push the updates from the trunk branch that git-svn handles
  • origin/with-git-submodules is the same as origin/master, but with submodules that refer to the IzPack utilities and native launcher (rationale: Git is not able to push back to Subversion when there are submodules…).

We need to reset our local master branch to origin/master as git-svn has put it on trunk:

git reset --hard origin/master
HEAD is now at c23f4e5 Merge branch 'local-trunk'

We can now create a few local branches:

git checkout -b local-trunk trunk
git checkout -b with-git-submodules origin/with-git-submodules
git checkout master

which gives:

jponge:izpack.git julien$ git branch -a
  local-trunk
* master
  with-git-submodules
  origin/master
  origin/vendor-svn-trunk
  origin/with-git-submodules
  trunk

That’s it, you can work with Git. Getting updates from SVN is easy (git svn fetch). It is easy to push back those updates from trunk to origin/vendor-svn-trunk. It is also easy to commit using git svn dcommit.

Git + SVN gives a really great flexibility for developing new features and collaborating with non-developers. Indeed, I can ultimately push back the work from Git to Subversion. I still use Subversion directly for many tasks such as committing a patch, but for new features working with Git is really appealing ;-)