A script to manage Git branches and worktrees using fzf in bash and zsh. It provides a
convenient way to handle Git branches and worktrees with a fuzzy finder interface.
- List, delete, and switch to Git branches and worktrees.
- Integrated with
fzffor enhanced user interaction. - Support for confirmation dialogs.
- ANSI output coloring for better readability.
git(https://git-scm.com/)fzf(https://github.com/junegunn/fzf)- Modern version of
bashorzsh - GNU coreutils - optional; required only for the
relative-cwd,relative-home,relative-repo,relative-gitdirandrelative-wt-basepath display modes (realpath --relative-to). The defaulttildemode,absolute,absolute-gitdirandtilde-gitdirdo not need it, and neither doesworktree add. Stock macOS ships BSD coreutils; install withbrew install coreutilsif needed.
-
Clone the repository or download the script.
git clone https://github.com/awerebea/fzf-git-branches.git ~/.fzf-git-branches -
Source the script in your shell configuration file (
.bashrc,.zshrc, etc.):source ~/.fzf-git-branches/fzf-git-branches.sh
-
Ensure
fzfis installed and available in yourPATH.
Options that can be defined using environment variables may also be specified in a configuration
file located at: $HOME/.config/fgbrc
The default values of FZF options are set as follows:
--height 80% \
--reverse \
--ansi \
--bind=ctrl-y:accept,ctrl-t:toggle+down \
--border=top \
--cycle \
--multi \
--pointer='' \
--preview 'FGB_BRANCH={1}; git log --oneline --decorate --graph --color=always \${FGB_BRANCH:1:-1}'These defaults can be overridden by setting the FGB_FZF_OPTS environment variable.
The default branches sort order is -committerdate, but this can be overridden by setting the
FGB_SORT_ORDER environment variable.
Similarly, the default date format is committerdate:relative, which can be overridden using
FGB_DATE_FORMAT.
Lastly, the default author format is committername, could be redefined with FGB_AUTHOR_FORMAT.
The default worktree path display mode is tilde, but this can be overridden by setting the
FGB_WT_PATH_DISPLAY environment variable. Available modes:
| Mode | Description |
|---|---|
tilde (default) |
Absolute path with $HOME collapsed to ~ |
absolute |
Full absolute path |
relative-cwd |
Path relative to the current directory |
relative-home |
Path relative to $HOME (e.g. ~/Github/project.git/wt/branch) |
relative-repo |
Path relative to the repo root (for bare repos: same as relative-gitdir) |
relative-gitdir |
Path relative to the git common dir (e.g. ./wt/my-branch) |
relative-wt-base |
Path relative to the worktree base dir (e.g. ./my-branch) |
absolute-gitdir |
Absolute path (a path under the git common dir re-joins to itself) |
tilde-gitdir |
Same as absolute-gitdir but with $HOME collapsed to ~ |
A path that is only parent segments is shown with a trailing slash, so ., .. and
../.. display as ./, ../ and ../../.
The default worktree base path template is ./wt, anchored to the git common dir:
- Bare repo (
project.git/): worktrees land atproject.git/wt/<branch> - Regular repo (
project/): worktrees land atproject/.git/wt/<branch>
Set FGB_WT_BASE_PATH_BARE and/or FGB_WT_BASE_PATH_REGULAR to customize.
A leading ~ and any $VAR references are expanded first; what remains is used as-is
when it starts with /, and anchored to the git common dir otherwise. . and ..
segments are collapsed, so a template that escapes the anchor leaves none behind.
Supported placeholders: {repo_name} (basename of the repo's working-tree root, which
for a bare repo is the bare directory itself, e.g. project.git) and
{repo_name_short} (same with a trailing .git stripped).
# Organize worktrees in a per-project subdir inside the bare repo:
FGB_WT_BASE_PATH_BARE=./wt/{repo_name_short}
# => project.git/wt/project/feature-x
# Restore the old outside-the-repo default (pre-v0.19):
FGB_WT_BASE_PATH_BARE=../worktrees/{repo_name_short}
FGB_WT_BASE_PATH_REGULAR=../../worktrees/{repo_name}
# Absolute, ~ and $VAR forms all work:
FGB_WT_BASE_PATH_REGULAR=~/Work/worktrees/{repo_name}
FGB_WT_BASE_PATH_BARE=$WORKTREES/{repo_name_short}To reduce shell startup time, consider lazy loading the script by calling it instead of sourcing it
automatically every time. Replace source ~/.fzf-git-branches/fzf-git-branches.sh in your shell rc
file with the following code snippet. This snippet defines several functions and aliases that load
the script only when needed:
# Check if the script is installed
if [ -f "$HOME/.fzf-git-branches/fzf-git-branches.sh" ]; then
lazy_fgb() {
unset -f fgb gbl gbm gwl gwa gwm gwt lazy_fgb
if ! source "$HOME/.fzf-git-branches/fzf-git-branches.sh"; then
echo "Failed to load fzf-git-branches" >&2
return 1
fi
alias gbl='fgb branch list'
alias gbm='fgb branch manage'
alias gwl='fgb worktree list'
alias gwm='fgb worktree manage'
alias gwa='fgb worktree add --confirm'
alias gwt='fgb worktree total --confirm'
fgb "$@"
}
function fgb() {
lazy_fgb "$@"
}
function gbl() {
lazy_fgb branch list "$@"
}
function gbm() {
lazy_fgb branch manage "$@"
}
function gwl() {
lazy_fgb worktree list "$@"
}
function gwm() {
lazy_fgb worktree manage "$@"
}
function gwa() {
lazy_fgb worktree add --confirm "$@"
}
function gwt() {
lazy_fgb worktree total --confirm "$@"
}
fiThis snippet defines a lazy loading function lazy_fgb and related functions that wrap the
lazy_fgb function call with corresponding commands, subcommands, and any additional arguments
provided:
lazy_fgb: The main function responsible for lazy loading fzf-git-branches.sh and executing commands based on arguments passed to it.fgb: Callslazy_fgbwith any arguments.gbl: Callslazy_fgbwith the commandbranch list.gbm: Callslazy_fgbwith the commandbranch manage.gwl: Callslazy_fgbwith the commandworktree list.gwm: Callslazy_fgbwith the commandworktree manage.gwa: Callslazy_fgbwith the commandworktree add --confirm.gwt: Callslazy_fgbwith the commandworktree total --confirm.
Here’s how it works:
Lazy Loading Function lazy_fgb:
On its first call, lazy_fgb unsets itself and all related functions.
It then sources the fzf-git-branches.sh script to load its functionality.
Aliases for Convenience: To replace the functions that were unset earlier, lazy_fgb establishes
aliases with identical names corresponding to commonly used commands provided by the script. These
aliases simplify the execution of the script’s commands, enhancing usability and efficiency.
Customization: Users can edit or expand the aliases as needed for their specific requirements.
This approach enhances shell startup efficiency by loading scripts only when necessary, while the predefined aliases streamline command execution once the script is loaded.
To start using the script, call the fgb function in your terminal with a command and its options
as arguments.
Manage branches:
fgb branch manageManage worktrees:
fgb worktree totalThis will open a fzf interface to manage your Git branches.
Default key bindings that can be overridden by FGB_FZF_OPTS environment variable:
enter/ctrl-y: Select the branch/worktree to jump to.ctrl-t: Toggle the selection.
After invoking fzf, the following key bindings are expected (and can be redefined by the
FGB_BINDKEY_DEL, FGB_BINDKEY_EXTEND_DEL, FGB_BINDKEY_INFO, FGB_BINDKEY_VERBOSE,
FGB_BINDKEY_NEW_BRANCH, FGB_BINDKEY_NEW_BRANCH_VERBOSE
environment variables respectively):
-
ctrl-d: Delete the selected branch. -
ctrl-alt-d: Extended delete. When deleting a worktree, delete the associated local branch; when deleting a local branch, delete the remote branch. -
ctrl-o: Show branch information. -
ctrl-v: Use verbose mode to prompt for user confirmation of the directory name for the new worktree, even when this confirmation is suppressed by the-c, --confirmoption. -
alt-n: Create a new branch by forking the currently selected (highlighted) branch and assigning it a specified name. Forworktreecommands creates a new worktree for this branch at the same time. -
alt-N: Create a fork of the selected branch and a worktree with a non-default path, even if the-c, --confirmoption suppresses confirmation of the worktree path.
Selecting a remote branch creates a worktree for its local counterpart, since
git worktree add <path> <branch> resolves a name to the existing local branch:
- No local branch yet - git creates one tracking the remote branch and checks it out in the new worktree.
- The local counterpart already has a worktree - the remote branch shows that
worktree in the
WTcolumn, since that is where selecting it goes, and fgb jumps there instead of failing withfatal: '<branch>' is already used by worktree at .... Such branches are therefore not offered byworktree add. If that worktree is behind the remote branch, fgb says so and offers to reset it first, defaulting to no; since the branch is checked out there, the reset is agit reset --hardin that worktree, so any uncommitted changes are listed in the prompt before you answer. The link is the branch's configured upstream, so it holds even when the local branch is named differently from the branch it tracks; when it is, the jump message names the branch that is actually checked out there. If several local branches track one remote branch and more than one has a worktree, the one named like the remote branch wins, and otherwise the alphabetically first - branch names are unique, so the choice is stable. - The local counterpart exists, has no worktree, and is behind the remote - the new worktree would check out the older local state. fgb reports how far behind it is and offers to reset the local branch to the remote branch first, defaulting to no. Declining creates the worktree from the local branch as before. If the local branch also has commits the remote does not, the prompt says how many a reset would discard.
ctrl-o (info) shows the resolved worktree for a remote branch too, naming the owning
local branch when that is not simply the remote branch without its remote prefix.
<remote>/HEAD is not listed: it is a symbolic ref pointing at another branch rather
than a branch of its own, and selecting it would create a detached worktree.
- Submodules are not supported. A submodule's git directory lives inside the
superproject's, at
<super>/.git/modules/<name>, and worktree base paths are anchored to the git common directory - so a submodule's worktrees would be created under the superproject's.git, invisible to normal tooling and removed bygit submodule deinit.fgb worktreedetects this and exits with a message instead. Clone the submodule as a standalone repository to use worktrees with it. - Column widths are measured in characters, not display cells. Branch names or author names containing double-width characters (CJK, some emoji) can shift the columns to the right of them.
- A repository created with
--separate-git-dirrecords nothing that points back to its main working tree, so whenfgbis run from one of its linked worktrees the main worktree's path cannot be determined; git reports its git directory instead. Every other worktree is unaffected.
-
fgb branch list [args]: Lists the Git branches in the repository and exit. -
fgb branch manage [args]: Switch to existing branches in the git repository, delete them, or get information about branches.
-
fgb worktree list [args]: Lists all worktrees in a Git repository and exit. -
fgb worktree manage [args]: Switch to existing worktrees in the Git repository or delete them. -
fgb worktree add [args]: Add a new worktree based on a selected Git branch. -
fgb worktree total [args]: Total control over worktrees. Add a new one, switch to an existing worktree in the Git repository, or delete them, optionally with corresponding branches.
Note: Worktree commands work in both bare and regular Git repositories.
-
By default, all commands list only local branches.
-
-r, --remotes: Lists only remote branches. -
-a, --all: Lists both local and remote branches. -
-s, --sort: Sort branches by :-
-committerdate(default ) -
refname -
authorname -
etc.
You can specify multiple sort criteria separated by commas (e.g.,
-committerdate,committername).In such cases:
- Branches will first be grouped alphabetically by the last specified criterion (e.g., committer name in the example).
- Within each group, branches will then be sorted based on the preceding criteria (e.g., by committer date in reverse order in the example).
-
-
-f, --force: Suppress confirmation dialog for non-destructive operations -
-c, --confirm: Automatic confirmation of the directory name for the new worktree -
-d, --date-format: Format for 'date' string:committerdate:relative(default)%(authordate) %(committerdate:short)authordate:(relative|local|default|iso|iso-strict|rfc|short|raw)authordate:format:'%Y-%m-%d %H:%M:%S'committerdate:format-local:'%Y-%m-%d %H:%M:%S'
-
-u, --author-format: Format for 'author' string:committername(default)authoremail%(committername) %(committeremail)%(authorname) %(authormail) / %(committername) %(committeremail)
-
-p, --wt-path-display(worktree commands only): Controls how worktree paths are displayed in the list:tilde(default) - absolute path with$HOMEcollapsed to~absolute- full absolute pathrelative-cwd- relative to the current directory (requires GNU coreutils)relative-home- relative to$HOMEwith~/prefix (requires GNU coreutils)relative-repo- relative to the repo root; for bare repos same asrelative-gitdir(requires GNU coreutils)relative-gitdir- relative to the git common dir (requires GNU coreutils)relative-wt-base- relative to the worktree base path (requires GNU coreutils)absolute-gitdir- git common dir prefix + relative path (requires GNU coreutils)tilde-gitdir- same asabsolute-gitdirwith$HOMEcollapsed to~(requires GNU coreutils)
For more details on each command and its options,
you can use the -h or --help option. For example:
fgb branch manage --helpA self-contained suite lives in tests/. It needs nothing beyond git and the
shell being tested - no test framework, and no network access:
./tests/run.sh # runs under every shell found (bash and zsh)
./tests/run.sh --in-shell # runs once, in the current shellIt builds throwaway repositories under a temporary directory (ordinary, bare,
--separate-git-dir, a superproject with a submodule, and a clone with
remote-tracking branches) and exercises the script's internal functions
directly, since the normal entry points open fzf. The temporary directory is
removed on exit and the run exits non-zero if any check fails.
Running under both shells is the point of the default mode: several past bugs were bash-only or zsh-only.
This script is licensed under the GPL License. See the LICENSE file for more details.
Feel free to open issues or submit pull requests if you find bugs or have suggestions for improvements.
Inspired by fzf-marks and git-worktree.nvim.











