Adding a command to list recently changed git branches (three ways)

I wanted to add a command to make it easy for me to see what branches I'd worked on recently. Between different projects, bugfixes, and code reviews, I end up switching branches a lot, and it can be hard to remember the names of the branches I need to polish up for submitting as a pull request and merging. It's also useful to find branches that can be deleted.

This is a git command to show all the branches sorted by date descending (branch names may be changed to protect the innocent):

git for-each-ref --sort -committerdate refs/heads/

1b46ebacca9d6a4f49698b59577e21254471f6ac commit refs/heads/look/fp-email-on-first-completion
875686cd2b9db4fe45b24a96299f71933f6599fa commit refs/heads/master
9ef1d512fcc6e05dc5b80121e1e3768c011bb167 commit refs/heads/look/document-position-metadata
ed6df9b73e2c38d74ee3419c71ee488703c8cb27 commit refs/heads/look/fix-dp-500
7b130aceaeafe31077265b6a1b7cdabd1f8329d4 commit refs/heads/look/improve-document-position-caching
fcb726b5372255a3b1c50eac1c04cf5eda4587d4 commit refs/heads/look/remove-rails-4-caching-hacks
bd1f3df97cfd3dcbe9803770fd24944bed01f236 commit refs/heads/look/remove-rails-3-code
9b6bc693e5273cf3a7ddea1062462ae333c1bb6a commit refs/heads/look/fix-selenium-rails-4
c231b29f083b14d7cdb698e8f4d2be105bc32e45 commit refs/heads/look/fix-account-router
8d16a3868f8a12961ee8dbcaa383fae7a10ebcd8 commit refs/heads/look/handle-invalid-content-hash
fe50ecee84e6017dfa444bb426c4ac7c9929fa50 commit refs/heads/look/unknown-format-rails-4
3bfe056947a351151d0e3cf18a61f30e9e03209b commit refs/heads/look/pluck
e903be6d8f2a2a170d913b5e77801600ec49023d commit refs/heads/look/match-array
bb07270a5045c737f60530ca1d3b8b17e2cdf7bb commit refs/heads/look/fix-change-column
bca2a817b32395576fe2c10be53b02022689ba75 commit refs/heads/look/rails-4-fixes
70c6e053c89e066f1eed0038faa1834ca255b4d4 commit refs/heads/look/GET_LOCK-sucks
07176fb95a802a3ca5dfcce5d3757d9831ef7d52 commit refs/heads/look/refactor-query-execution
5cb599fc57c7729ea8ed76f62ccd930b774a4d9c commit refs/heads/engine_index_refactor
(... dozens of branches omitted ...)

Adding a bit of formatting makes and piping to head makes the output easier to read:

git for-each-ref --format='%(committerdate:iso8601) %(committerdate:relative) %(refname)' --sort -committerdate refs/heads/ | head -15    

2016-08-04 17:59:50 -0700 4 days ago refs/heads/look/fp-email-on-first-completion
2016-08-04 17:24:34 -0700 4 days ago refs/heads/master
2016-08-04 15:02:23 -0700 4 days ago refs/heads/look/document-position-metadata
2016-08-02 13:27:42 -0700 6 days ago refs/heads/look/fix-dp-500
2016-07-28 12:24:30 -0700 11 days ago refs/heads/look/improve-document-position-caching
2016-07-19 19:55:07 -0700 3 weeks ago refs/heads/look/remove-rails-4-caching-hacks
2016-07-19 18:45:06 -0700 3 weeks ago refs/heads/look/remove-rails-3-code
2016-07-19 18:07:56 -0700 3 weeks ago refs/heads/look/fix-selenium-rails-4
2016-07-19 15:03:15 -0700 3 weeks ago refs/heads/look/fix-account-router
2016-07-18 17:36:24 -0700 3 weeks ago refs/heads/look/handle-invalid-content-hash
2016-06-30 18:49:33 -0700 6 weeks ago refs/heads/look/unknown-format-rails-4
2016-06-29 19:18:17 -0700 6 weeks ago refs/heads/look/pluck
2016-06-29 19:17:18 -0700 6 weeks ago refs/heads/look/match-array
2016-06-29 11:17:49 -0700 6 weeks ago refs/heads/look/fix-change-column
2016-06-21 14:36:50 -0700 7 weeks ago refs/heads/look/rails-4-fixes

This is a lot of typing, though! The easiest way to shorten it is to create a Bash alias:

alias grecent="git for-each-ref --format='%(committerdate:iso8601) %(committerdate:relative) %(refname)' --sort -committerdate refs/heads/ | head -15"

However, I wanted to make this a new subcommand, because I have trouble remembering to use Bash aliases.

The first thing I looked into was using a Bash function:

function git() {
    if [[ $@ == 'recent' ]]; then
        command git for-each-ref --format='%(committerdate:iso8601) %(committerdate:relative) %(refname)' --sort -committerdate refs/heads/ | head -15
    else
        command git "$@"
    fi
}

This intercepts invocation of the git command. If the argument is recent, it runs my command. Otherwise, it passes the arguments to the git command (the Bash command builtin is used so it can call the real git). One thing I learned writing this is that getting the quoting right is super important.

That's not the only way to do it, though!

You can also add a git alias. Since this version doesn't take an argument, that's fairly simple:

[alias]
        recent = !git for-each-ref --format='%(committerdate:iso8601) %(committerdate:relative) %(refname)' --sort -committerdate refs/heads/ | head -15

Because I only want to show some of the recent items, I need to invoke the alias as a shell command using ! so I can use head. You can also add arguments to git aliases, but it is a bit more complicated.

Another way is to add a new executable named git-recent to your $PATH (Antonio Santos Velasco has a good tutorial about this.). This will enable you to type git recent to invoke it. I have a ~/bin directory that is on my path, so that is a pretty easy place to stash such a command. If you want to add argument parsing (for example, to change the default number of recent items), using a stand-alone script is probably the easiest way.

Here's the full git-recent script:

#!/bin/bash

DEFAULT_ITEMS=15
ITEMS=${1-$DEFAULT_ITEMS}

git for-each-ref --format='%(committerdate:iso8601) %(committerdate:relative) %(refname)' --sort -committerdate refs/heads/ | head -$ITEMS

You can invoke it like this to get the default number of recent branches:

git recent

Or specify the number of branches:

git recent 10

Now that I have an easy way of listing recently changed branches, I use it constantly. It's been super useful for me. Maybe it will be for you, too.