March 7, 2014
At Endocode we use Gerrit as our code review and repository management tool. As is often the case, the default Gerrit setup was unsuitable for us. There were mainly two reasons for this. Firstly, there was no way to create our work branches where we can do as we please (direct pushes, forced pushes). Lack of work branches stems from Gerrit’s default access configuration.
The second issue was that there was no way to browse the code easily. A code browser is very useful for viewing changes in a context larger than just what a diff shows you. It’s also handy for pasting links into email or IRC for colleagues.
The low hanging fruit
Solving the first problem is just a matter of properly configuring Gerrit’s “All-Projects” access, which is used as a base for other projects. It should define the widest, yet still sane, permissions. Projects inheriting from “All-Projects” can then narrow or widen these permissions even more if needed.
In our case, we configured “All-Projects” refs/ as read-only, refs/heads/ as reviewable, verifiable and submitable, refs/for/refs/ as pushable and refs/wip/ as a free-for-all (all permissions granted). There are also other references configured, but they are internal to Gerrit and not relevant here.
The latter problem of configuring Gerrit with a code browser is a solved one. Consult Gerrit’s documentation about gitweb to get that working. However, as we’ll see soon, it was only of limited use until we made a few changes and got them upstream.
Teaching gitweb new tricks
So far, everything was rather straightforward. After setting up Gerrit and gitweb, two more problems appeared. The first was that for this configuration gitweb wasn’t showing branches in namespaces other than refs/heads/, meaning we could not view the work branches. The second problem, which we’ll look at in the next section, was that our Git workflow had gotten a bit more complicated.
Since Git (and thus gitweb) are free software, the first of these issues could be solved by cloning the Git repository, creating patches and sending them to Git’s mailing list for consideration in the upstream project. This we did, and after several roundtrips it was accepted into mainline.
The recent Git 1.9 release is the first stable version containing our changes. Documentation for this new feature can be found in the gitweb.conf manpage or online (search for extra-branch-refs).
The results of all this can be seen in the following screenshot. Notice the feature-1 and feature-2 branches with the refs/wip namespace clearly marked. Gitweb is no longer restricted to showing branches in refs/heads/.
A typical workflow
Recall that the second issue we ran into was that our Git workflow had become a bit more complicated. To illustrate this let’s look at a typical Git workflow:
git clone ssh://firstname.lastname@example.org/project.git project cd project # this creates a new branch tracking the branch # in remote repository git checkout existing-feature-branch # do some hacking git commit -a git push # some time later, code is ready to be reviewed git push existing-feature-branch:refs/for/master
or, in case one is creating a new feature branch (after cloning, of course):
git checkout -b new-feature-branch git push --set-upstream new-feature-branch # do some hacking git commit -a git push # some time later, code is ready to be reviewed git push new-feature-branch:refs/for/master
In our setup we need to make take additional steps:
git clone ssh://email@example.com/project.git project cd project # maybe download some hooks and whatnot for Gerrit # make sure that git pull will download # our work branches as well git config --add remote.origin.fetch \ '+refs/wip/*:refs/remotes/origin-wip/*' git fetch origin # this creates a new branch tracking the branch # in remote repository git checkout existing-feature-branch # do some hacking git commit -a git push # some time later, code is ready to be reviewed git push existing-feature-branch:refs/for/master
or, in case of creating a new feature branch:
git checkout -b new-feature-branch git config branch.new-feature-branch.remote origin git config branch.new-feature-branch.merge \ refs/wip/new-feature-branch # do some hacking git commit -a git push # some time later, code is ready to be reviewed git push new-feature-branch:refs/for/master
Unfortunately, –set-upstream flag does not work in our case, that’s why we have to dabble with the git config manually. But we are already talking with Git maintainers, so maybe somewhere in the future this will be fixed too.
As we can see, our (admittedly uncommon) setup was not and still is not particularly supported by Git, but we are working on it. Fortunately, the one-time complications in our workflows can be hidden with Git’s aliases. So, we could have aliases like
git gclone <projectname> [optional_project_directory] git work-branch <new-work-branch-name>
Figuring out how above aliases are defined and what scripts used by them are written is left as an exercise for a reader.