Checkout git branches faster with fzf

In large git repo’s I find myself sometimes lost in switching branches. For this project I work on using Bitbucket we use branch names starting with a JIRA issue number. This makes relying on Tab-completion harder. I ain’t got time to remember JIRA issue numbers.

This is where fzf comes in, a fuzzy finder for the command-line. You can find the GitHub repo here.

You can pipe lines to fzf and it gives you an interactive input prompt you can use to filter.

$ git branch | fzf
> juice
  29/29
>   XY-8435-create-juice-model
    XY-5193-fix-juice-form-fields

The above example just outputs the branch name you select. In this case it filtered the branches that were listed using git branch on the keyword juice.

In order for this to be somewhat useful you need to combine it with a git checkout. In order to do so you can use the following. Where the output of $(git branch | fzf) is used as argument to the checkout subcommand.

$ git checkout $(git branch | fzf)
Switched to branch 'master'

So this already does the job. As you see above you can switch branches like this. There is a problem with this however and is has to do with the outputs of git branch.

$ git branch
  develop
* master

As you see the current branch is marked with an asterisk(*). Eventhough it is unlikely I would want to try to switch my current branch. Actually I would never want that. But just to make it prone to typo’s, lets fix it.

$ git checkout $(git branch | fzf)
error: pathspec 'master' did not match any file(s) known to git

If I would select master, my current branch, in fzf it would give me the error shown above. Since the output of fzf contains the asterisk too. See the snippet bellow. Where I selected master in fzf.

$ git branch | fzf
* master

So we have to get the branch names in some other way to not have the asterisk showing up. In git we can use a lesser known, for-each-ref command and format the output as follows.

$ git for-each-ref refs/heads/ --format='%(refname:short)'
master
develop

I can use that command as a substitute for git branch. If I now try to switch to my current branch I get a more appropriate message.

$ git checkout $(git for-each-ref refs/heads/ --format='%(refname:short)' | fzf)
Already on 'master'
Your branch is up to date with 'origin/master'

Cool, that works too! So we are finished but it turned into one hell of a command which I don’t plan on typing out ever. Instead I created a separate script.

For me this script is at ~/Code/bin/interactive-checkout, this works for me because I have the directory ~/Code/bin added to my path.

The script interactive-checkout has the following contents.

#!/bin/bash

git checkout $(git for-each-ref refs/heads/ --format='%(refname:short)' | fzf)

Now I can use interactive-checkout as a command instead of git checkout $(git for-each-ref refs/heads/ --format='%(refname:short)' | fzf) and I can potentially bind it to an alias as well.

Don’t forget to make the script executable with chmod :-).

See Also

6 Non-Programming Books for Programmers