Benjamin Esham

Listing the last person who committed to each branch in a Git repository

Here is a command that will list all of the server branches for the current Git repository, ordered from least recently edited to most recently edited, and with the latest author shown for each:

git branch -r --sort=creatordate \
    --format "%(creatordate:relative);%(committername);%(refname:lstrip=-1)" \
    | grep -v ";HEAD$" \
    | column -s ";" -t

If the column command is not available on your system,1 you can replace it with

    | sed -e "s/;/\t/g"

for a similar effect. Note also that you will need Git 2.13 (released in May 2017) or later.

Using the Jekyll repository as an example,2 the output will look like

6 years ago             Tom Preston-Werner  book
4 years, 4 months ago   Parker Moore        0.12.1-release
4 years ago             Matt Rogers         1.0-branch
3 years, 11 months ago  Matt Rogers         1.2_branch
3 years, 1 month ago    Parker Moore        v1-stable
12 months ago           Ben Balter          pages-as-documents
10 months ago           Jordon Bedwell      make-jekyll-parallel
6 months ago            Pat Hawks           to_integer
5 months ago            Parker Moore        3.4-stable-backport-5920
4 months ago            Parker Moore        yajl-ruby-2-4-patch
4 weeks ago             Parker Moore        3.4-stable
3 weeks ago             Parker Moore        rouge-1-and-2
19 hours ago            jekyllbot           master


My most recent project at work had several contributors from multiple teams. I took it upon myself to periodically prune our branches, which meant that I needed to know who was responsible for each branch. BitBucket didn’t seem to show that information anywhere so I rigged up this command.

(By the way, I highly recommend GitUp for macOS if you’re interested in a novel way of visualizing your branches:

A screenshot of GitUp.

Be sure to turn on the options to show stale branch tips and remote branch tips.)

How it works

The git command lists all of the branches on the server,3 ordered from least recently edited to most recently edited. For each branch, it prints the relative timestamp of the latest commit; the name of the author of the latest commit; and the branch name. The grep command removes the “HEAD” pointer from the list, since it’s probably just pointing to one of the other branches in the list and we don’t need to show that branch twice. Finally, the column command puts the information into a nice tabular form.

  1. column is part of BSD, so it’s available on macOS. It’s available under Ubuntu if the “bsdmainutils” package is installed, which it seems to be by default. ↩︎

  2. I’ve omitted some of Jekyll’s branches for brevity. ↩︎

  3. To be precise, it lists all of the remote-tracking branches. If your local copy of the repo is up to date then this is the same as “all of the branches on the server.” ↩︎

What I believe

Sarah Kenzidor recently said, “Write a list of things you would never do. Because it is possible that in the next year, you will do them. Write a list of things you would never believe. Because it is possible that in the next year, you will either believe them or be forced to say you believe them.” Well, we’re almost over the line, so now is as good a time as any.

I reject Donald Trump as President of the United States. (Later today he will be the President — I don’t dispute that — but his sneering disregard for the formal and informal rules of the office make him unfit for it.)

I reject authoritarianism and fascism.

I reject Trump’s contempt for the free press.

I reject the supremacy of cis, straight white men, even though I am all of those.

I reject the notion that anyone is not a “real American” because they are well-educated or well-off or liberal or live in a city.

I reject lying, whether shameless or subtle. I reject gaslighting. I reject the idea that truth is a meaningless concept. I reject anti-intellectualism. I reject climate-change denialism.

I reject the Electoral College. I reject our “first past the post” voting system. I reject the two-party system that they enforce. I reject the voter suppression that may have helped Trump to win. I reject the idea that Republican Party unity is more important than the health of the country.

I reject intolerance. I reject the idea that intolerance is an opinion as valid as any other.

I reject the assumption that a free society has capitalism as its core. I reject the notion that unfettered capitalism is even compatible with free society. I reject letting “the market” determine people’s health-care options, or their fates.

I reject racism. I reject bigotry. I reject sexual assault.

I refuse to accept that humanity is no better than this. I know that — eventually — we will do better.

Day One’s lack of encryption is crippling it for me

Two years ago I wrote about how I use Day One, the journalling app for iOS and macOS. At the time I used it for reflective “how I’m feeling” pieces, notes about fun things I was doing, and occasionally a photo of food. A year later Bloom Built released the second major version of Day One. This version brought many improvements but it also dropped support for Dropbox syncing in favor of a homegrown syncing service called Day One Sync.

I trusted the people at Dropbox to store my data securely. While I’m sure that the Bloom Built engineers have the best of intentions, the company simply doesn’t have the same level of security expertise. Therefore, I don’t trust Day One Sync with my journal — my most private of data — and so it lives only on my phone now. In turn, this means that the longer entries I would have typed on my laptop have mostly gone unwritten. Those were the introspective, “journally” pieces, so now my usage of Day One is mostly to record what I’m eating. That’s a disappointing turn of events.

The security of Day One Sync will be much less of an issue once Bloom Built adds some encryption features. If I can encrypt my journal before it gets synced, with a password that only I know, then it doesn’t really matter if the Day One Sync server is breached: the hackers would only be able to see the encrypted version of my journal (and they wouldn’t have my password in any form, hashed or otherwise). Bloom Built is working on this feature but they haven’t given any estimate of when it might be ready. Until then, my journal is reduced to the kinds of entries that are short enough that I can peck them out on my phone’s keyboard.

HTTP 410 Gone But Not Forgotten

When I first launched this blog I used FeedBurner to handle its RSS feed. FeedBurner is — was — a proxy that would serve your site’s RSS feed unmodified but record a bunch of analytics as it did so. (I was hosting this site on Amazon S3, which didn’t have any real way to do server-side logging or analytics.1) The way it worked was that you would publish an RSS feed at some publicly-accessible URL, point FeedBurner to that URL, and then give out FeedBurner’s proxied URL instead of your original one.

A couple of years ago I started hosting this site on a “real” web server and I no longer needed to use FeedBurner. One downside of relying on this third-party service became clear: my few subscribers had FeedBurner’s URL, not mine, saved in their feed readers. Even if I could get FeedBurner to emit an HTTP redirect — I couldn’t — my subscribers’ feed readers would probably continue to request the FeedBurner feed indefinitely.

I did the best thing I could think of, which was to point FeedBurner to a dummy RSS feed that contained a single item: a note explaining that you were subscribed to the FeedBurner version of my feed and requesting that you subscribe to the new, “real” feed instead.

A little over a year ago I figured that this notice had been available for long enough. Apparently forgetting that I could just log in to FeedBurner and delete the feed, I set my web server to give an HTTP 410 “Gone” response when the FeedBurner feed was requested. (This status code indicates that “the target resource is no longer available at the origin server and that this condition is likely to be permanent.”)

For the next twelve months, FeedBurner dutifully kept trying to fetch my dummy feed, never losing hope that the 410 Gone would one day be replaced by a beautiful 200 OK. Not only that, but when I finally remembered that I could just log in to FeedBurner and delete the damn thing, the health check told me that everything was sunny:

Screenshot of FeedBurner’s “FeedMedic” tool, reporting that my feed had been “quite healthy.”

“Quite healthy” seems like a weird way to say “There is no feed content and I get an error when I try to request it.”

  1. Maybe Amazon has better options now, but at the time I think the only way to log the activity on your S3 website was to have it spit out (into another S3 bucket) log files with one or two events per file. This produced an unmanageable number of files — even with my very modest traffic — and the files being stored on S3 didn’t help. ↩︎

Never Again

Yesterday I signed the pledge at I, and 584 other members of the tech industry, have committed not to collaborate with the upcoming Trump administration by helping to create databases of people’s race, religion, or national origin. We will advocate that our companies collect as little of this data as possible; that they discard existing caches as quickly as they can; and that they refuse to turn data over to the government without a lawful order. We commit to push back if our companies collect, store, or release users’ data in an illegal or unethical way.

Signing this pledge, of course, is the easy part. As I quipped on Twitter, this really was the least I could do. Living up to the pledge will be the hard part — although, truthfully, I’m much less likely than some of the other signatories to find myself in a position where I need to speak out at work. If I do, though, it will be infinitely easier knowing that so many others in the tech community are behind me.

  1. This seems to be the name of both the website and the pledge. It doesn’t exactly roll off the tongue, does it? ↩︎

Subtweeting without tweeting

Earlier this month, Nate Silver tweeted something that made me do a double take. He described a piece by New York Times columnist Paul Krugman as being “basically a subtweet of NYT’s campaign coverage.” The column, of course, wasn’t a tweet at all, but here was a perfectly erudite person calling it a subtweet.

“Subtweet,” a portmanteau of “subtext” and “tweet,” refers to a negative tweet about some subject that cattily avoids actually mentioning that subject.1 Mulling over Silver’s statement, I realized that I couldn’t think of another word to describe this stylistic device. “Subtext” itself refers to the hidden meaning, not the work that carries the hidden meaning. “Innuendo” refers to the latter, but is most often used for sex-related insinuations. (It’s also harder to work into a sentence: compare “a subtweet of their campaign coverage” to “an innuendo referring to their campaign coverage.”)

I eventually came to the same conclusion as Silver: although “subtweet” explicitly invokes Twitter, there’s simply no better word for the concept. The existing vocabulary was so lacking that the word has escaped its roots and become generally applicable.

(I was reminded of this subject again today when I read this review by Michiko Kakutani of a new Hitler biography. It’s a pretty masterful piece of, well, subtweeting.)

  1. Sorry for mansplaining the word “subtext” to you. ↩︎

A nifty fifty isn’t always the best choice for your first lens

The perennial advice for new SLR owners is to buy a “nifty fifty” lens: an inexpensive (but optically very good) lens with a fixed focal length of 50 mm. Most camera makers offer one of these for $100–150. They don’t zoom, but when you mount them on a full-frame camera they provide roughly the same field of view as the human eye, making them useful for many different styles of photography.

The operative phrase there, though, is “on a full-frame camera.” When you use a 50 mm lens on a crop-frame APS-C camera, the effective focal length is more like 80 mm in full-frame terms.1 APS-C cameras dominate the lower end of the SLR market, so the first-time buyer who’s being advised to buy a nifty fifty is probably going to be mounting it on a crop-frame camera.

That combination is much less versatile. To my taste it’s too “zoomed in” to be a general-purpose or walkaround lens, and while it’s a good focal length for portraits you have to be at least a couple of yards2 from your subject or else your composition will feel cramped. Fixed-focal-length (“prime”) lenses may have better optical quality than the zoom you buy with your camera, but that’s irrelevant if the lens has too narrow a field of view for you to get the photos you want.

If you’re buying your first SLR and you’re considering a prime lens to go with it, you might want to spend a couple of weeks shooting with the kit zoom first. These have ranges like 18–55 mm, so you can just set it on 50 mm and pretend it’s stuck there. Is that a comfortable focal length for you? If yes, great! My nifty fifty became one of my most-used lenses once I learned what it was good for (and what it wasn’t). If you find that a different focal length better suits your style, though, you might be glad you didn’t unnecessarily constrain yourself to 50 mm right off the bat.3

  1. The “effective focal length” can be found by multiplying 50 mm by the crop factor of your camera. For APS-C cameras these factors range from 1.52 to 1.7. ↩︎

  2. A couple of meters. ↩︎

  3. The obvious solution here seems to be to buy a prime lens in the 28–32 mm range, which would give you a field of view equivalent to 50 mm on full-frame. That’s true; there just aren’t any lenses in that category that are quite as good a value as the nifty fifty. (Not in the Canon system, anyway, as far as I know.) ↩︎