Fuzzy Find in the Terminal for Git
Using Git
In this post, I’ll share how I use fzf
to fuzzy find and open files in my Editor. I also have a post on how I use fzf
to speed up working with Git on the command line, where I explain a bit more about how fzf
works. Feel free to jump straight to either how I use fzf
with Git log or Git checkout.
In my day to day work as a software engineer, I spend a good amount of time performing Git actions in the terminal. I’ve been using the built-in oh-my-zsh aliases to speed this up a bit (e.g., typing gco
instead of git checkout
), but I recently tweaked my setup with two fzf
-enabled aliases that have been really helpful.
I’ve added this tool to my workflow for a few things that I do very often, especially with quickly finding and opening files and some Git
management. In this post, I’ll share two of these that I use so much I’ve added them as short aliases so they’re really easy to reach for: finding Git logs and finding and automagically checking out Git branches.
Fuzzy Finding Git Logs
I very, very often look at Git logs - mostly to try and find a commit where someone did something. Usually it’s possible to find where a change was introduced based on the commit message, but it’s not always quite clear what that would look like. For instance, if I made a change to the list controller, did I say changes list controller
, changes ListController
, or changes ListController.cs
? Yes, there should be a standard and yes, these messages should be well thought-through, but these are difficult things to control in a team and under time crunches; also, things change as projects go on! So being able to fuzzy find through the Git logs is really helpful. And with fzf
, this is easy:
1
git log | fzf
And since I’m already used to the oh-my-zsh
alias glo
(which stands for git log --oneline --decorate
), I added glof
as an alias:
1
alias glof="glo | fzf"
Then I realized I actually wanted this to grab the commit and copy it to the clipboard. That worked like this:
1
glo | fzf | awk 'NR==1{print $1}' | pbcopy
The awk
command would grab only the first word (of the first line) and copy it to the clipboard using pbcopy
. But now, the commit is only in the clipboard, and it’s not being printed to the screen. And this is where I learnt about tee
, which I’ve never used before. Essentially tee
duplicates the output so it is sent to the terminal and to a file, but the “file” part here is pretty loose and can be substituted with another command. In this case, we’ll process the input with awk
like we did before, and send that to pbcopy
. Additionally, we’ll add a step to remove the end of line character (which was still being copied in the previous command).
1
glo | fzf | tee >(awk 'NR==1{print $1}' | tr -d '\n' | pbcopy)
Now the whole line of the Git log is being printed to the terminal, but only the commit hash (with no end of line character) is copied to the clipbard. Finally, to make this work as an alias, those extra \
s are needed again to escape the special characters:
1
alias glof="glo | fzf | tee >(awk 'NR==1{print \$1}' | tr -d '\\n' | pbcopy)""
Fuzzy Finding Git Branches and Checking Out
Something I might even do more often than looking at Gig logs is checking out different branches. This can easily get a bit challening, even with tab completion, good naming conventions, and regular clean-up of your local Git branches (not that I’m a shining example in the latter). Switching between fixing bugs in the current development code, someone else’s branch to get some context of the changes in their PR, tracing some issue that’s visible in production, and the current project I’m working on could all happen in a relatively short period of time - and sometimes remembering what branch I was actually working on takes a minute.
For a while now, I’ve been using gb --sort=-committerdate
to find the most recent branches that I’ve worked on. (gco
here is oh-my-zsh
shorthand for git checkout
). As the somewhat long-winded flag implies, this sorts the branches by the date of their last commits. The -
is needed because the default for this sort is to show the oldest commit first; adding -
lists the most recent commit first (which is almost always what I want).
This works well, and generally gets me to the correct branch. But I realized that I could speed this up a bit. Every time I do git branch
, it’s generally because I want to switch to that branch, not because I’m interested in the history of my project. And thankfully, there are no spaces in the branch names, so no extra filtering is needed - we can pass the fzf
result straight into git checkout
:
1
git branch --sort=-committerdate | fzf | xargs git checkout
Of course, that’s just one step away from adding the alias gcof
(again, no extra filtering or escapes needed for this one):
1
alias gcof="git branch --sort=-committerdate | fzf | xargs git checkout"
Now gcof
opens fzf
with a drop-down list of most recently worked on branches, which after selecting will be checked out for me. (If you don’t want to switch, you can just press the esc
key to exit fzf
).