As promised in my last post, the first tool to make working with git easier is all about search.

Fzf is an interactive search tool written in Go. Fzf can take any list of strings and drop you into a search view where you can do some basic regex style search.

To start off, let’s install fzf.

# assuming you're using Ubuntu
sudo apt-get install fzf

So, what did I just apt-get?

Run fzf without any arguments to launch the default file search. It’ll pick up everything under the directory you’re in - recursively. You can also get to this by the keyboard shortcut Ctrl+T. Fzf indexes the data each time it runs - there’s no caching for this (on purpose). But it lets you start searching before it has finished this process and the results update as it finds more matches. This might sound problematic but it’s blazingly fast - usually done before I type out my query text. Alt+C does the same but limits to directories and cd s into the one you select.

If you just type in some text, fzf will filter down to items containing the text, highlighting the matched substrings. Use Ctrl+J to move down and Ctrl+K to move up through the options. Enter selects something and exits fzf. You can also use Tab and Shift+Tab to select/deselect multiple items. You can also do a ReSharper-like search by just typing out the capital letters. $ and ^ can be used as markers for end and beginning of string as well.

Fzf also takes over the history search for commands you’ve run. Ctrl+R lets you find a command that you’ve run before. Why is this better than partial command search or tab-completion? Because I can now search for commands based on arguments I used - like show me everything I recently did to branch X or file Y.

Show me more …

One of the arguments you can use with fzf is a preview-command. It will run the command you supply on the currently selected item and show you the results in a short preview window on the side. This command could be anything from showing me the contents of the file to showing me the history of changes to the file.

Sweet right? But we can do so much more. Fzf like any well behaved unixy tool is composable, which means we can build our own commands with it. But before we get into that …

A short detour

I also post this blog internally at work and my last post got a lot of comments. Thanks to some of these, I started looking deeper into using PowerShell as my primary shell and I have to say it’s been very promising. I had no idea PowerShell was available on all platforms now. I’m now trying it out at work and home, and I hope to port over all my tools. What can I say I’m easily swayed … by people making sense. :)

PowerShell is my new favourite hammer

So in that spirit, I got fzf setup in PowerShell (yep, it’s written in Go so it’s cross-platform too!).

choco install fzf
Install-Module PSFzf
"`nImport-Module PSFzf" | Out-File -Append -Path $Profile

Back to our regular programming

Let’s get fzf doing more for us now. The creator of fzf, Junegunn Choi, actually wrote versions of some of these but I couldn’t get them working as-is so I wrote my own. I’ll use my newly ported PowerShell functions for this post but I have the original shell scripts for this if anyone wants them.

Branch selector

This function grabs all the branches (local and remote) and pipes them to fzf. It then strips the selected text to just the branch name and returns it back. I name my branches prefixed by work item number, so searching by the text after the number is extremely useful. The preview window shows me a log of commits between master and the branch I currently have selected. The last bit at the end registers Ctrl+G+B as the keyboard shortcut to trigger this. This is something I was never able to sort out in shell script, so 50 points to house PowerShell!! What’s handy about this is I can now write a command that needs a branch ref like git checkout or git show or git diff and have fzf fill in the branch names for me!

Function Invoke-FuzzyBranchSelector() {
  If (!(Get-GitDirectory)) { Return } # Get-GitDirectory provided by posh-git

  # grab all branch names
  # -> strip spaces, asterisk and carets from start of line item
  # -> line item may look like branch -> origin/branch, strip away the second half
  $branchListing = $(git branch -a 2>$null) `
    | ForEach-Object { $_.TrimStart('* ^').Split(' ')[0]  }

  # define what to show in the preview window
  $previewCommand = @"
    git -C $(Get-Location) log --color --oneline master..`{`}
"@

  # interactively select a branch
  $selectedBranch = $branchListing `
    | Invoke-Fzf -Preview $previewCommand -Exact -NoSort -PreviewWindow 'right:70%'

  If ($null -eq $selectedBranch) { Return }

  # remove the remote qualifiers from the branch name
  $(git remote) | ForEach-Object { $selectedBranch = $selectedBranch.Replace("remotes/$_/", '') }

  Return $selectedBranch
}

Set-PSReadLineKeyHandler -Chord Ctrl+G, Ctrl+B -ScriptBlock {
  param($key, $arg)

  $selectedBranch = $(Invoke-FuzzyBranchSelector)
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$selectedBranch")
}

Commit selector

You can probably see where this is going, so I won’t flood this blog post with code snippets. I’ve posted my PowerShell profile script here instead. https://gitlab.com/snippets/1770382

But just to describe this one, it lets me quickly see at a glance what commits have been made recently and a short preview of their contents. If you change the log command to include the author, you could search using that too.

Solution/SQL launcher

This one is probably the one I use the most. The function is fairly straight forward, all it does is invoke fzf with an initial search condition - file names should end in .sln. Once I select the solution files I care about it pipes the path to explorer.exe which then knows to launch a Visual Studio instance for each.

The SQL launcher works similarly but since VSCode is my default handler for SQL files, I get an instance of that instead.

That’s all for this post - hope someone finds this useful. Do let me know if you have questions or think up some other cool use-cases!