Git, Gerrit and TeamForge
In this blog, I will show you how to use GitEye with Gerrit, the most widely used code review and permissions management framework for Git. Although GitEye will work with any Gerrit server, I will be using TeamForge for Git. TeamForge extends and wraps Gerrit, adding centralized role-based access control (RBAC) and greatly simplifying management of Gerrit access rights. It also provides powerful features such as history protection, making Git ready for the enterprise.
The first thing I will do is clone my TeamForge Git repository using the context menu from the CollabNet Sites view.
The pages of the Clone Git Repository wizard will be automatically filled, an example of how the GitEye graphical interface simplifies all Git operations.
A Gerrit repository is a wrapper for a regular Git repository. It prevents unauthorized users from pushing directly to the Git repository. Instead, a user who wants to submit a code change for review will push the change to a special path using the ref specification /refs/for/master. A push to /refs/for/master triggers Gerrit to create a new review request or add a new patch set to an existing review request, depending on the Change-Id information in the commit message.
After cloning a Gerrit repository, the next step is to configure the local repository for Gerrit. To do this, I expand the repository in the Git Repositories view. Then I expand the Remotes node, right-click on origin and select Gerrit Configuration… from the context menu.
Once again, the dialog will be pre-filled with the values that I want and all I have to do is click Finish.
What we have done is updated the push configuration to make refs/for/master the default ref specification to use when pushing changes. To confirm this, I can expand origin, right-click on the push configuration node, and select Configure Push… from the context menu.
As you can also see in the above screenshot, the repository icon has now been decorated to indicate that it is configured for Gerrit.
The first thing that I do when I am ready to work on a change is create a branch for the change in my local repository. I right-click the repository and select Switch To > New Branch… from the context menu.
On the Create Branch dialog, click Select…
Select origin/master under the Remote Tracking node and click OK.
Since I am using a TeamForge tracker and associate a work item, or artifact, with all my commits, it makes sense to use my artifact ID as the name for my new branch. I will also check Configure upstream for push and pull and I will select Rebase commits of local branch onto upstream.
I leave Checkout new branch selected and click Finish. I am now working on the artf1103 branch.
In the above steps we have not only created and checked out a new branch, but we have configured Pull so that it will rebase our local commits onto upstream (origin/master). If I am going to be working on my changes for an extended time, I will pull frequently.
If other changes have been pushed to the remote master and perhaps caused conflicts with my local commits, I would prefer to find out about it and deal with it sooner rather than later (such as when it is time for my change to be merged).
Let’s assume now that I have made some changes to source code and would like to request a review. I will right-click on the repository and select Commit… from the context menu.
Notice the Change-Id row in the commit message. When we configured the repository for Gerrit, a flag was added to the repository that tells GitEye to add this automatically. The Change-Id is what uniquely identifies a Gerrit change. I0000000000000000000000000000000000000000 is a special value used to indicate a new Gerrit change. When the commit takes place, this will be replaced by the actual ID that is generated by Git.
I will go ahead and click Commit and Push. Because the repository has been configured for Gerrit, I can accept all the default values on each page of the push wizard.
Once the changes are pushed to Gerrit, a change request is created. I can view and work with it from either the Gerrit web ui or from directly in GitEye, which includes a rich change request editor. In this case I have gone to the CollabNet Sites view and expanded my TeamForge server node and the Gerrit node. Then I expanded one of the built-in Gerrit queries to reveal the change request. When I double-click the request, or right-click it and select Open with > Editor, it is opened in the Gerrit change request editor.
Once my change request has been created I can wait patiently for authorized users to review it, or I can click on Add Reviewers… and invite specific users to do a review. In the above screenshot, I’ve done the latter.
Let’s suppose that the user who I have invited has reviewed my changes using Gerrit’s side-by-side display and determined that something is missing. He clicks on the Review utton (this time I am showing the Gerrit web UI).
Then he gives the change a -1 and adds a comment explaining why he would prefer that the change not be submitted.
There are a few things worth noting in the above screenshot.
- The word submit in this context basically means merge. When a change is approved and submitted, it is merged into the code base. In this case, since the reviewer is voting against the change, he will click Publish Comments.
- A user with sufficient permissions can give a change a +2 and submit it right away, but it is normally preferable that at least two users review a change before it is merged.
- A -1 vote is typically used to indicate that you agree with the change in general, but you think that it still needs work before being submitted. A -1 vote is cleared when the change is updated with a new patch set. A -2 vote, however, is not removed when a new patch set is uploaded. It is typically used to indicate that you disagree with the change in principal, and it remains unless it is explicitly revoked.
- There are two sections where a vote can be cast, the Verified section and the Code-Review section. In this blog I am focusing on the Code-Review section. This is where peer review of your code takes place. A vote in the Verified section does not mean that the code makes sense, that it does what it’s supposed to do or that it adheres to your project’s coding standards. It simply means that it works, which typically equates to “it does not break the build”. With TeamForge, Gerrit can be configured such that a pushed change automatically triggers a Jenkins build and the Jenkins build in turn automatically casts a -1 or +1 verification vote depending on whether the build fails or succeeds. If you are interested in learning more about how TeamForge, Gerrit and Jenkins work in concert, you will find an excellent webinar here.
Update Review with New Patch Set
Having been notified that my changes need some work, let’s assume that I’ve read the reviewer’s comments (which can be at the change level, but can also be associated with individual lines of code) and I know exactly what I need to fix in my code. However, take a close look at my workspace.
While I was waiting for my changes to be reviewed, I have not been sitting on my hands. I have created a new branch (artf1104) for another work item that I have been assigned, and I have started to do work in that branch. Before I fix the problems that were found during code review, I will switch back to the branch from which I pushed the change.
Now let’s assume that I have fixed my code. Again, I right-click on the repository and select Commit…
This time, and this is very important because Gerrit will reject a push of a commit if it contains the same Change-Id as a predecessor commit, I click on the Amend Previous Commit icon (first icon above the commit message). The commit message is populated with my previous commit message, with the Change-Id resolved, as shown above. When I click Commit and Push, a second patch set will be added to my change request.
This time my change is approved.
When the reviewer clicks Publish and Submit, Gerrit merges my change into the code base.
At times it is useful to pull a change from Gerrit into a local repository. Perhaps, as a reviewer, I want to run the actual code locally and maybe step through the code in debug mode. Or perhaps the user who made the change request is no longer available to work on it, or is not qualified to make the follow up changes suggested by reviewers.
To pull a change from Gerrit using the command line, I would do something like this:
git pull ssh://firstname.lastname@example.org:29418/eclipse_desktop refs/changes/05/5/1
The GitEye UI, however, makes this very easy. I right-click on the repository and select Fetch from Gerrit…
In the Fetch a Change from Gerrit dialog, I can use Ctrl+Space in the Change field to select from a list of existing changes. When I select a change, the dialog fields will be filled in so as to generate the appropriate fetch request, including a logical name for the local branch to be created and checked out.
Once I have the change, I can tweak it as necessary. When I am ready, I will amend the previous commit, as shown earlier in this blog, and push to Gerrit, resulting in a new patch set being added to the change.
If you are adding a new patch set to another developer’s change request, as described above, then you should be sure to coordinate this activity with the original author so that you don’t both end up doing the same (or conflicting) work.
If I am authorized to do so, I can skip code review and push directly to the code base. To do this, I right-click on the repository and select Remote > Push…
When the push wizard is shown, click Next to go to the Push Ref Specifications page.
Click the remove icon to remove the refs/for/master spec.
In the Source ref dropdown, select the master branch. The screen will look like this:
Click the Add Spec button to add the ref specification.
Leave the Save specifications in ‘origin’ configuration checkbox unselected. We only want to override this particular push to go directly to the code base; we want the default ref specification to remain refs/for/master so that future pushes will continue to result in Gerrit change requests.
Click Finish to push directly to the code base.
I hope this blog will be helpful if you are getting started with Gerrit and GitEye. Before closing, there is one last important thing that I would like to stress.
One patch set corresponds to exactly one Git commit.
Lack of understanding of this concept is a primary source of confusion and frustration for many if not most new Gerrit users.
If you want to commit your work periodically as you prepare a change request, then always amend the previous commit!
If you have forgotten to or chosen not to do this and your changes now consist of multiple commits, then you must squash these commits before you can push to Gerrit. You do this using interactive rebase. Interactive rebase is beyond the scope of this article but, since GitEye is built on top of EGit, you can learn about it in the EGit user guide, here.