A better ebuild workflow with pure git and pkgcheck

Many developers today continue using repoman commit as their primary way of committing to Gentoo. While this tool was quite helpful, if not indispensable in times of CVS, today it’s a burden. The workflow using a single serial tool to check your packages and commit to them is not very efficient. Not only it wastes your time and slows you down — it discourages you from splitting your changes into more atomic commits.

Upon hearing the pkgcheck advocacy, many developers ask whether it can commit for you. It won’t do that, that’s not its purpose. Not only it’s waste of time to implement that — it would actually make it a worse tool. With its parallel engine pkgcheck really shines when dealing with multiple packages — forcing it to work on one package is a waste of its potential.

Rather than trying to proliferate your bad old habits, you should learn how to use git and pkgcheck efficiently. This post aims to give you a few advices.

pkgcheck after committing

Repoman was built under the assumption that checks should be done prior to committing. That is understandable when you’re working on a ‘live’ repository as the ones used by CVS or Subversion. However, in case of VCS-es involving staging commits such as Git there is no real difference between checking prior to or post commit. The most efficient pkgcheck workflow is to check once all changes are committed and you are ready to push.

The most recent version of pkgcheck has a command just for that:

$ pkgcheck scan --commits

Yes, it’s that simple. It checks what you’ve committed compared to origin (note: you’ll need to have a correct origin remote), and runs scan on all those packages. Now, if you’re committing changes to multiple packages (which should be pretty common), the scan is run in parallel to utilize your CPU power better.

You might say: but repoman ensures that my commit message is neat these days! Guess what. The --commits option does exactly that — it raises warnings if your commit message is bad. Admittedly, it only checks summary line at the moment but that’s something that can (and will) be improved easily.

And I’ve forgotten the most cool thing of all: pkgcheck also reports if you accidentally remove the newest ebuild with stable keywords on given arch!

One more tip. You can use the following option to include full live verification of URLs:

$ pkgcheck scan --net --commits

Again, this is a feature missing entirely from repoman.

pkgcommit to ease committing to ebuilds

While the majority of repoman’s VCS support is superficial or better implemented elsewhere, there’s one killer feature worth keeping: automatically prepending the package name to the summary line. Since that is a really trivial thing, I’ve reimplemented it in a few lines of bash as pkgcommit.

When run in a package directory, it runs an editor with pre-filled commit message template to let you type it in, then passes it along with its own arguments to git. Usually, I use it as (I like to be explicit about signoffs and signing, you can make .git/config take care of it):

$ pkgcommit -sS .

Its extra feature is that it processes -m option and lets you skip the editor for simple messages:

$ pkgcommit -sS . -m 'Bump to 1.2.3'

Note that it does not go out of its way to figure out what to commit. You need to either stage changes yourself via git add, or pass appropriate paths to the command. What’s important is that it does not limit you to committing to one directory — you can e.g. include some profile changes easily.

You’ll also need pkg script from the same repository. Or you just install the whole bundle of app-portage/mgorny-dev-scripts.

Amending commits via fixups

Most of you know probably know that you can update commits via git commit --amend. However, that’s useful only for editing the most recent commit. You can also use interactive rebase to choose specific commits for editing, and then amend them. Yet, usually there’s a much more convenient way of doing that.

In order to commit a fixup to a particular past commit, use:

$ git commit --fixup OLD_COMMIT_ID

This will create a specially titled commit that will be automatically picked up and ordered by the interactive rebase:

$ git rebase -i -S origin

Again, I have a tool of greater convenience. Frequently, I just want to update the latest commit to a particular package (directory). git-fixup does exactly that — it finds the identifier of the latest commit to a particular file/directory (or the current directory when no parameter is given) and commits a fixup to that:

$ git-fixup .

Note that if you try to push fixups into the repository, nothing will stop you. This is one of the reasons that I don’t enable signoffs and signing on all commits by default. This way, if I forget to rebase my fixups, the git hook will reject them as lacking signoff and/or signature.

Again, it is part of app-portage/mgorny-dev-scripts.

Interactive rebase to the rescue

When trivial tools are no longer sufficient, interactive rebase is probably one of the best tools for editing your commits. Start by initiating it for all commits since the last push:

$ git rebase -i -S origin

It will bring your editor with a list of all commits. Using this list, you can do a lot: reorder commits, drop them, reword their commit messages, use squash or fixup to merge them into other commits, and finally: edit them (open for amending).

The interactive rebase is probably the most powerful porcelain git command. I’ve personally found the immediate tips given by git good enough but I realize that many people find it hard nevertheless. Since it’s not my goal here to provide detailed instructions on using git, I’m going to suggest looking online for tutorials and guides. The Rewriting History section of the Git Book also has a few examples.

Before pushing: git log

git log seems to be one of the most underappreciated pre-push tools. However, it can be of great service to you. When run prior to pushing, it can help you verify that what you’re pushing is actually what you’ve meant to push.

$ git log --stat

will list all staged commits along with a pretty summary of affected files. This can help you notice that you’ve forgotten to git add a patch, or that you’ve accidentally committed some extraneous change, or that you’ve just mixed changes from two commits.

Of course, you can go even further and take a look at the changes in patch form:

$ git log -p

While I realize this is nothing new or surprising to you, sometimes it’s worthwhile to reiterate the basics in a different context to make you realize something obvious.