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
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
$ 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 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
$ 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.
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