Skip to content

Latest commit

 

History

218 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fzf-git-branches

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.

Features

  • List, delete, and switch to Git branches and worktrees.
  • Integrated with fzf for enhanced user interaction.
  • Support for confirmation dialogs.
  • ANSI output coloring for better readability.

Requirements

  • git (https://git-scm.com/)
  • fzf (https://github.com/junegunn/fzf)
  • Modern version of bash or zsh
  • GNU coreutils - optional; required only for the relative-cwd, relative-home, relative-repo, relative-gitdir and relative-wt-base path display modes (realpath --relative-to). The default tilde mode, absolute, absolute-gitdir and tilde-gitdir do not need it, and neither does worktree add. Stock macOS ships BSD coreutils; install with brew install coreutils if needed.

Installation

  1. Clone the repository or download the script.

    git clone https://github.com/awerebea/fzf-git-branches.git ~/.fzf-git-branches
  2. Source the script in your shell configuration file (.bashrc, .zshrc, etc.):

    source ~/.fzf-git-branches/fzf-git-branches.sh
  3. Ensure fzf is installed and available in your PATH.

Configuration

Options that can be defined using environment variables may also be specified in a configuration file located at: $HOME/.config/fgbrc

Screenshot

image

Advanced Configuration

Default Options Overriding

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 at project.git/wt/<branch>
  • Regular repo (project/): worktrees land at project/.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}

Lazy Load

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 "$@"
    }
fi

Lazy Load Explanation

This 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: Calls lazy_fgb with any arguments.
  • gbl: Calls lazy_fgb with the command branch list.
  • gbm: Calls lazy_fgb with the command branch manage.
  • gwl: Calls lazy_fgb with the command worktree list.
  • gwm: Calls lazy_fgb with the command worktree manage.
  • gwa: Calls lazy_fgb with the command worktree add --confirm.
  • gwt: Calls lazy_fgb with the command worktree 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.

Usage

To start using the script, call the fgb function in your terminal with a command and its options as arguments.

Examples

Manage branches:

fgb branch manage
Screenshot

image

Manage worktrees:

fgb worktree total
Screenshot

image

This will open a fzf interface to manage your Git branches.

Key Bindings

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.

    Screenshot

    image

  • ctrl-alt-d: Extended delete. When deleting a worktree, delete the associated local branch; when deleting a local branch, delete the remote branch.

    Screenshot

    image

  • ctrl-o: Show branch information.

    Screenshot

    image

  • 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, --confirm option.

    Screenshot

    image

  • alt-n: Create a new branch by forking the currently selected (highlighted) branch and assigning it a specified name. For worktree commands creates a new worktree for this branch at the same time.

    Screenshot

    image

  • alt-N: Create a fork of the selected branch and a worktree with a non-default path, even if the -c, --confirm option suppresses confirmation of the worktree path.

Remote Branches

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 WT column, since that is where selecting it goes, and fgb jumps there instead of failing with fatal: '<branch>' is already used by worktree at .... Such branches are therefore not offered by worktree 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 a git reset --hard in 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.

Known Limitations

  • 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 by git submodule deinit. fgb worktree detects 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-dir records nothing that points back to its main working tree, so when fgb is 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.

Available Commands and Subcommands

Branch Commands

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

Worktree Commands

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

Available options used in commands in appropriate combinations.
  • By default, all commands list only local branches.

  • -r, --remotes: Lists only remote branches.

    Screenshot

    image

  • -a, --all: Lists both local and remote branches.

    Screenshot

    image

  • -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 $HOME collapsed to ~
    • absolute - full absolute path
    • relative-cwd - relative to the current directory (requires GNU coreutils)
    • relative-home - relative to $HOME with ~/ prefix (requires GNU coreutils)
    • relative-repo - relative to the repo root; for bare repos same as relative-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 as absolute-gitdir with $HOME collapsed to ~ (requires GNU coreutils)
Screenshot

image

For more details on each command and its options, you can use the -h or --help option. For example:

fgb branch manage --help
Screenshot

image

Tests

A 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 shell

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

License

This script is licensed under the GPL License. See the LICENSE file for more details.

Contribution

Feel free to open issues or submit pull requests if you find bugs or have suggestions for improvements.

Inspiration

Inspired by fzf-marks and git-worktree.nvim.

About

Script to manage Git branches and worktrees in bash and zsh

Resources

Stars

4 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages