Checkout git branches faster with fzf
October 7, 2020 ‐ 3 min read
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 :-).