Git aliases for Gerrit-based workflow

After using Gerrit professionally for almost 5 years, I have came up with a set of 6 simple git aliases to ease things up.

One does not push force

I now really like Gerrit compared to Github and Gitlab-based merge requests workflow. One of the main reasons is that Gerrit allows you to refine your commit using multiple patchsets, until is is good enough to be merged. In the PR/MR way, from my observations, you push many fix commits into your development branch — or eventually push-force to your branch a refined commit.

This PR/MR workflow leads to commit pollution because of all of the extra fix commits and merge commits, or hard-to-review PR/MR if a force push is used because the interface does not keep track of “patchset revisions” like Gerrit.

The Gerrit flow however comes with an extra cost: having to put a Change-Id trailer in every commit message. For me this is fine as long as doing closed-source work. When upstreaming, one has to remember to remove these trailers from the patchset.

Pushing to review

You basically interact from Git to Gerrit using two commands:

  • Creating a review: git push origin HEAD:refs/for/your-branch%topic=your-topic
  • Updating a review: git push origin HEAD:refs/for/your-branch

Most of your work will be targeting the master branches; probably main for newer repositories with an up-to-date Gerrit version (or not, depends on upstream and company policy). This assumption allows us to create the first alias for updating reviews to the master branch:

# update gerrit, master
ug = git push origin HEAD:refs/for/master

If your want to update to a branch other than main, we can create another alias that can take a parameter. Because git aliases is very basic, it does not allow us to compose strings. This is fine if you want to pass arguments as-is to other programs, but we will need to make a bit of string concatenation for our next alias to work. Hopefully, on Debian systems sh is dash and is very fast to fork, so we can use it to concatenate the strings:

# update gerrit, custom branch
ugb = "!sh -c \"git push origin HEAD:refs/for/$1\""

Now that we have this trick, we can create two other aliases for creating reviews by taking the topic as a parameter:

# create gerrit, master
cg = "!sh -c \"git push origin HEAD:refs/for/master%topic=$1\""
# create gerrit, custom branch
cgb = "!sh -c \"git push origin HEAD:refs/for/$1%topic=$2\""

Final aliases

This provides us with the final aliases to work comfortably with Gerrit:

cg = "!sh -c \"git push origin HEAD:refs/for/master%topic=$1\""
cgb = "!sh -c \"git push origin HEAD:refs/for/$1%topic=$2\""
ug = git push origin HEAD:refs/for/master
ugb = "!sh -c \"git push origin HEAD:refs/for/$1\""

Here are two bonus aliases, just for your eyes (and for my future self):

fix = commit --amend --no-edit
fixmodules = "!sh -c \"git st --porcelain | awk '/M/ {print \\$2}' | tee | xargs -n1 git submodule update\""

The first one is because you will be reworking the same commit again and again, instead of pushing fix commits. So better get used to do this quickly.

The last one will fix out-of-date checked-out submodules only. Using git submodule update would update all of the submodules, even the ones that are not currently checked out, and this is will take hours on our system.