CLI tool
junegunn/fzf-git.sh avatar
junegunn/fzf-git.sh

fzf-git.sh: paste Git objects into your command line with fzf

bash, zsh, fish, and tmux bindings for Git objects, powered by fzf

1,198 stars100 forksShellMIT

At a glance

What is it?
fzf-git.sh is a set of shell bindings that use fzf to browse branches, tags, remotes, commit hashes, stashes, reflogs, worktrees and every ref, then paste the selection into the command line as if you had typed it. There are separate files for bash, zsh, fish and tmux, and the README is unusually specific about the key conflicts you will hit.
Who is it for?
Adopt fzf-git.sh if you work in Git from the command line every day and want branch, tag and commit selection to be a fuzzy search rather than a tab completion, and if you are willing to source one file from your shell configuration and learn the two key chords. Do not adopt it if your workflow lives inside a GUI client, since the whole value is in pasting into a shell prompt.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Selection that lands in the prompt as typed text

fzf-git.sh is a set of bash, zsh, fish and tmux key bindings for Git objects, powered by fzf. It comes from the author of fzf itself, and the repository contains exactly what you would expect from that: fzf-git.sh for bash and zsh, fzf-git.fish for fish, and fzf-git.tmux for tmux, plus the README and the licence.

The interaction model is the thing to understand first. Each binding lets you browse through Git objects of a certain type, and select the objects you want to paste to your command line. Not to run a command, not to change a branch, but to paste the object into the prompt so that the next command in the line is composed by you.

That is a small design choice with a large effect. Because the selection becomes text in the prompt, the same binding works for checkout, for log, for diff, for branch deletion, for anything that takes a ref as an argument. You are not learning a set of commands, you are learning a set of completions.

The project is MIT licensed, the primary language is Shell, and the last push was on 2026-09-27. There are no GitHub releases, which is normal for a file you source from your shell configuration rather than a package you install and upgrade.

Three ways to install it, all of them a source line

Installation has no package manager step and no build. There are three files and you source the right one.

You need the latest version of fzf installed. Optionally, bat gives you syntax-highlighted file previews, and that is the only optional dependency. There is also a version requirement worth knowing: Git v2.42.0 or later is required for the git for-each-ref binding, which is the one that lists every ref rather than the standard categories.

For bash or zsh, source the fzf-git.sh file from your .bashrc or .zshrc. For fish, source fzf-git.fish from your config.fish. For tmux, run fzf-git.tmux from your .tmux.conf, and the documented line is:

sh
# ~/.tmux.conf
run-shell /path/to/fzf-git.tmux

The tmux file expects to find fzf-git.sh next to it, so the instruction is to keep the two files in the same directory. That is a small detail that will bite you if you download the tmux file alone, and it is the kind of thing a symlink into a dotfiles repository satisfies neatly.

Sourcing rather than installing is the right pattern here because the bindings are shell functions and shell aliases. They have to exist in the shell you are typing in, and there is no daemon or plugin manager that could make that work for you.

Nine bindings, all behind CTRL-G

The binding list is short and mnemonic, and every one of them is a chord starting with CTRL-G.

CTRL-G followed by question mark shows the list of bindings. CTRL-G followed by F is for files, CTRL-G followed by B is for branches, CTRL-G followed by T is for tags, CTRL-G followed by R is for remotes, CTRL-G followed by H is for commit hashes, CTRL-G followed by S is for stashes, CTRL-G followed by L is for reflogs, CTRL-G followed by W is for worktrees, and CTRL-G followed by E is for each ref, which is the git for-each-ref binding that needs Git 2.42.0 or later.

One binding behaves differently from the rest. The files binding works outside a Git repository as well, where it falls back to the file search of fzf. So the same key chord is useful in a directory that is not a repository, which is a small nicety that makes the bindings feel less special-cased.

Inside the fzf window there are three more keys. TAB or SHIFT-TAB select multiple objects, CTRL-/ changes the preview window layout, and CTRL-O opens the object in the web browser in the GitHub URL scheme, which turns a commit hash into a link to the commit page without leaving the terminal.

The conflicts, spelled out in the README

Most key binding tools fail in ways that are annoying to debug. This one documents the failures up front, in a warning block, and the list is short enough to memorise.

CTRL-G followed by B will not work if CTRL-B is used as the tmux prefix. That is a straight key conflict, and for anyone who has set tmux's prefix to CTRL-B, which is a common choice, the branches binding is simply gone.

CTRL-G followed by S will not work if flow control is enabled, because CTRL-S will freeze the terminal instead. The fix given is stty -ixon, which disables the flow control, or in other words turns off the software flow control that the terminal uses for pausing output.

Both have a documented workaround, which is to use CTRL-G followed by the lowercase key instead of CTRL-G followed by the uppercase key.

There is a third conflict that is specific to zsh. If zsh's KEYTIMEOUT is too small, for example 1, you may not be able to hit two keys in time, which means the chord does not register and the terminal just sees two separate keys. That is a different kind of problem, because it is a timing setting rather than a key mapping.

tmux bindings work in any pane, not just the prompt

The shell bindings have a limitation that the tmux bindings exist to solve. The shell bindings are only available on the command line, so they cannot be used when the pane is running another program. fzf-git.tmux binds the same list of commands to tmux keys, which work in any pane.

The keys are the same as the shell ones, with the tmux PREFIX and g in place of CTRL-G. So PREFIX followed by g followed by f is for files, and PREFIX followed by g followed by question mark shows the list. You can pick a different key after the prefix with the @fzf-git-key option:

sh
set -g @fzf-git-key G
run-shell /path/to/fzf-git.tmux

The option is read when the bindings are created, so it has to be set before the run-shell line. That ordering matters and is stated in the README.

fzf starts in a floating pane, in the working directory of the pane it was started from, and the selected objects are sent to that pane as if you typed them. A floating pane requires tmux 3.7 or later, and on older versions fzf starts in a popup instead.

There is a subtlety about configuration. The finder does not load your shell configuration file, so a _fzf_git_fzf redefined there has no effect, and the way to change it is the tmux environment:

sh
set-environment -g FZF_GIT_PAGER delta

The README also points at tmux-fzf-complete as the companion for the things that are not Git objects, files, directories, and the URLs, paths and sentences on the screen.

Customisation: one function and the commands behind it

Customisation happens in two places, and both are ordinary shell functions you write in your own configuration.

The first is _fzf_git_fzf, which is the function that runs fzf itself. Redefining it changes the options for every binding at once, and the documented example shows a compact fzf invocation with a half-height window, a tmux-aware position, reverse layout, multi-select, a minimum height, a header border, a border label position, a blue label colour, a preview window on the right at half width, and a key binding that changes the preview window between a lower position and hidden:

sh
_fzf_git_fzf() {
  fzf --height 50% --tmux 90%,70% \
    --layout reverse --multi --min-height 20+ \
    --no-separator --header-border horizontal \
    --border-label-pos 2 \
    --color 'label:blue' \
    --preview-window 'right,50%' --preview-border line \
    --bind 'ctrl-/:change-preview-window(down,50%|hidden|)' "$@"
}

The second is per-command. Each binding is backed by a _fzf_git_* function, so you can define your own shortcuts around them, for example a checkout helper and a worktree helper:

sh
gco() {
  _fzf_git_each_ref --no-multi | xargs git checkout
}

gswt() {
  cd "$(_fzf_git_worktrees --no-multi)"
}

The --no-multi flag is the important part, because the underlying functions support multi-select and you want a single ref for a checkout or a single directory to change into.

The environment variables cover the file preview and the colours. BAT_STYLE sets the style for displaying files with bat and defaults to full. FZF_GIT_CAT defines the preview command used for displaying the file and defaults to bat with the style and colour variables substituted. FZF_GIT_COLOR suppresses colours in the list when set to never and defaults to always. FZF_GIT_PAGER is the pager command for the preview window and defaults to whatever git config --get core.pager returns. FZF_GIT_PREVIEW_COLOR suppresses colours in the preview window the same way.

Editorial conclusion

Adopt fzf-git.sh if you work in Git from the command line every day and want branch, tag and commit selection to be a fuzzy search rather than a tab completion, and if you are willing to source one file from your shell configuration and learn the two key chords. Do not adopt it if your workflow lives inside a GUI client, since the whole value is in pasting into a shell prompt. Before you start, check the conflict list, because CTRL-B is unusable if tmux uses it as its prefix and CTRL-S freezes the terminal when flow control is on, and make sure the tmux file sits next to the shell file, since fzf-git.tmux expects to find fzf-git.sh beside it.

Frequently asked questions

How do I install fzf-git.sh?

Install fzf first, then source the right file from your shell configuration: fzf-git.sh from .bashrc or .zshrc, fzf-git.fish from config.fish, or run fzf-git.tmux from .tmux.conf with run-shell /path/to/fzf-git.tmux, keeping the tmux file next to fzf-git.sh. bat is optional and only affects file previews.

Which Git objects can fzf-git.sh browse?

Files, branches, tags, remotes, commit hashes, stashes, reflogs, worktrees and every ref via git for-each-ref, which needs Git 2.42.0 or later. The files binding also works outside a Git repository, where it falls back to the file search of fzf.

Why does CTRL-G CTRL-S freeze my terminal?

Because flow control is enabled, so CTRL-S is interpreted as a pause request by the terminal rather than as part of the key chord. The README gives stty -ixon to disable flow control, and notes that using the lowercase key instead of the uppercase one also works around it.

How do I use the fzf-git bindings inside tmux panes?

The shell bindings only work on the command line, so fzf-git.tmux binds the same commands to tmux keys that work in any pane, with PREFIX and g in place of CTRL-G. Set @fzf-git-key to change the key after the prefix, and set it before the run-shell line, since the option is read when the bindings are created.

How do I customise how fzf-git.sh searches?

Redefine the _fzf_git_fzf function in your shell configuration to change the fzf options for every binding, and define your own commands on top of the _fzf_git_* functions, using --no-multi when you want a single object, for example _fzf_git_each_ref --no-multi piped into git checkout.

Official sources

  1. Issues
  2. junegunn/fzf-git.sh on GitHub
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/junegunn-fzf-git-sh.svg)](https://hysenlabs.com/projects/junegunn-fzf-git-sh)

Was this useful?