When it comes to git history, less is more

Stephen Brennan • 15 June 2021

At a previous company, there was an “infamous” commit in our main repository. The commit was about 10 years old, and it replaced every tab with 4 spaces. When the commit was authored, the repository was likely in the hundreds of thousands, or maybe millions, of lines of code. For folks like me, who liked to go “spelunking” through git history, it was a frustrating barrier, but no more than a moderate frustration. It didn’t impact day-to-day work. After all, this repo was a regularly-deployed web application. Development happened on the master branch, deploys happened multiple times per business day, and old revisions were quickly forgotten.

When I started trying to contribute to the Linux kernel, the commonly suggested way to get started was to contribute coding style fixes, or to correct simple warnings from a static analyzer. But this came with some advice: try to keep your submissions within the drivers/staging directory, because many core kernel subsystem maintainers and reviewers don’t appreciate those trivial patches. I remember being quite frustrated at that. It felt quite unwelcoming to be restricted to what I saw as a “playground”, rather than working on “real kernel code.”

Now, my day-to-day work involves diagnosing, reproducing, and fixing bugs in Linux kernel versions which have been released for years. Unlike a web app, the kernel has long-term stable releases that continue to be supported long after the master branch has moved on. Fixing a bug on the master branch doesn’t magically fix all those released kernels; instead the fix must be backported to each older release.

In the simplest case, this “backport” could just be git cherry-pick, which simply applies a change to a different branch. However, if the surrounding code is at all modified, then the commit may not apply cleanly, and you’ll need to manually apply the changes and resolve conflicts. Now, it’s important to say that just because a commit applies cleanly to an older version of code, doesn’t mean that it’s a valid backport. For instance, a function you call may have changed its behavior, and some side-effect that your patch relied upon could no longer exist. But for the majority of the time, a clean cherry pick is likely a safe backport. And, each time you encounter a commit which doesn’t apply cleanly, your backport becomes much more labor-intensive.

So there’s a really strong incentive to make sure commits backport cleanly! The easier it is to backport, the more efficient it is to get bug fixes to users on the stable versions they actually use. Engineers can be more efficient and customers will be happier.

What’s really impressive to me is just how good the Linux community is at this. I’ve seen bugfixes apply cleanly to release branches from 10 years ago with no issues, something that would be completely impossible with the infamous tabs-to-spaces commit at my previous company.

Since starting the (slightly less glamorous) job of wrangling bugs in stable kernels, I’ve come to appreciate the discipline exercised by kernel maintainers. Their unwillingness to accept more trivial patches is something like a first line of defense. When patches fail to apply, it’s less likely to be due to small style refactors, and more likely to be due to real differences between versions that need to be addressed. This means that automated tools can handle more of the simple cases, leaving the difficult ones up to developers, without quite so much of the tedium.

I guess there are two morals to this story. First is that with experience comes perspective. I felt that the drivers/staging guideline was restrictive and maybe a bit elitist, but now that I understand the reasoning, it makes sense. Second is that, as always, there’s no silver bullet. It would be fun and punchy to end this post with the advice that “you should never do big code style changes” and “you should always discourage trivial, non-functional patches”. But for the regularly deployed web application with no legacy versions to maintain, that just doesn’t make sense. So instead, maybe this post can help provide some perspective on why the kernel is maintained the way it is.


Creative Commons License

Stephen Brennan's Blog is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License