fuzgit

Pick, search, and trace your way through git.

You should not have to remember a branch name or a commit hash to get work done. Every fuzgit subcommand follows the same model: build the candidate list, filter and pick it in a fuzzy finder (with a preview), then run the git operation. The fuzzy finder is skim, embedded as a library — no external fzf or sk binary is required.

package fuzgit  ·  command gz  ·  written in Rust  ·  MIT

Filter, preview, pick — nothing to remember

The two commands where a fuzzy finder pays off immediately: gz branch and gz stash. Type the fragment you do remember, let the preview confirm it is the right one, and press Enter to run the git operation.

gz branch --all — switching
$ gz branch --all
>  * main
     feature/login
     origin/feature/search
You never type a branch name: you filter the list until the one you meant is in front of you. The current branch carries the same leading * as git branch, and the preview shows the 50 most recent commits of the highlighted branch (git log --oneline --decorate). With --all, remote-tracking branches join the list — picking origin/feature/search switches by the short name, so git’s DWIM creates the tracking local branch.
gz stash apply — searching stashes
$ gz stash apply
>  stash@{0}: On main: half-done auth refactor
   stash@{1}: On feature/search: draft of the search form
Candidates for apply / pop / drop are stash@{n}: <message>, so you filter by what you wrote, not by index. The preview is git stash show -p --color=always, and drop — the one step that cannot be undone — asks for confirmation ([y/N]) first.
gz stash push — stash only what you picked
$ gz stash push -m "half-done auth refactor"
>> M  src/auth.rs
 > M  src/session.rs
   M  README.md
push picks files rather than a stash. Tab selects several of them and only the files you picked are stashed — everything you left alone stays in the working tree. Staged changes are stashed too, so the preview is the diff against HEAD (git diff HEAD); -u adds untracked files to the list.

Pick several targets, then let git do the work

The same model, except that what you pick is a set: gz fetch -s updates several repositories and gz pull brings several branches up to their upstream — one selection, one run, one summary.

gz fetch --siblings — the repositories next door
$ gz fetch --siblings
The current repository is preselected. Tab: toggle the selection / Enter: fetch  |  1 excluded (no remote / bare)
>> mike   origin/main
   alpha  origin/main
   zulu   origin/main
-s (--siblings) scans the parent directory of the current worktree root one level deep only and offers every directory that contains a .git; a line reads <directory> <remote>/<current branch>. Repositories that cannot be fetched are excluded, never silently — the number skipped is in the header.
gz pull — selection
$ gz pull
The current branch is preselected. Tab: toggle the selection / Enter: integrate with a fast-forward only  |  2 excluded (no upstream / remote not registered / in use by another worktree)
>> * main       →  origin/main
     feature-a  →  origin/feature-a
It asks only which local branches should follow their upstream — the destination is fixed to each branch’s own upstream, and integration is fast-forward only.
gz pull — run
$ gz pull
[1/4] main
[2/4] alpha
[3/4] diverged
[4/4] zeta
3 succeeded / 1 failed (failed: diverged)
A branch that could not fast-forward can be integrated with `gz sync --rebase` or `gz sync --merge` after switching to it
Targets run serially, in list order, each announced with [<n>/<total>] so you can tell whose output git is printing; a failure never stops the run, and the tally comes at the end. gz fetch --siblings reports the same way. fuzgit only points at the next step — it never starts gz sync for you.

Note: the terminal excerpts above show fuzgit’s English output, which is the default; the same lines come out in Japanese with --lang ja. Everything git prints (update tables, conflicts, authentication prompts) comes straight from git.

Design, not features

One model, every command

List candidates → filter and pick in the fuzzy finder with a preview → run the git operation. Learn it once and every subcommand behaves the same way.

Previews never hit the network

Candidate lists and previews are built from local repository data only. A preview is regenerated on every cursor move, so a network round trip there would block the whole display.

Only three commands go online

gz fetch, gz pull and gz sync use the network. Everything else is local — pushing is left to plain git push.

Built not to lose your work

Destructive operations confirm first, gz pull is fast-forward only, merge shows a conflict prediction, pushing is left to git and history is never rewritten automatically.

No shell, no injection surface

git is always executed with an argument array, never through a shell, and previews run git from Rust instead of handing skim a command string. Picked values are verified against the candidate list before they reach git.

Fast first paint

Candidates are read with gitoxide (gix). Anything that would need one git process per candidate — ahead/behind across repositories, for instance — is moved into the preview so the list appears immediately.

English or Japanese, decided in one place

Messages, confirmation prompts, finder headers and --help come in English and Japanese. English is the default, and a single git config entry makes it Japanese everywhere.

git config --global fuzgit.lang ja   # persistent (--local sets it per repository)
gz --lang ja branch                  # one-off, on any subcommand
The first layer that decides the language wins; the ones below it are not consulted.
Priority Source
1 --lang <ja|en|auto> — a global option, accepted by every subcommand
2 FUZGIT_LANG environment variable
3 git config fuzgit.lang — system / global / local / worktree all apply as usual
4 LC_ALLLC_MESSAGESLANGUAGELANG
5 fallback: en

Layers 1–3 are explicit instructions to fuzgit, so a value other than ja / en / auto stops with an error. Layer 4 only describes the environment, so a value fuzgit cannot interpret — C and POSIX included — is not an error and resolution moves on. auto skips the remaining explicit layers and reads the environment. There is no fuzgit configuration file: it borrows git’s fuzgit.lang key, which is read without starting a git process and works outside a repository too.

Two limits. fuzgit tells the git commands it runs which language to speak, but whether git has that translation is not guaranteed — it depends on the git build (NLS) and the installed locale data, and git upstream ships no Japanese catalog, so git’s own output stays English even with ja. And the text clap prints on its own (Usage:, Options:, Commands:, parser errors) stays English, because clap 4 has no localization hook; fuzgit’s own descriptions in --help do switch.

Commands

gz <subcommand> [options]. Running gz with no arguments — or gz --help — lists the subcommands.

Every row links to its section in the documentation.
Subcommand What it does Selection
gz branch Pick a branch and switch to it (subcommands also create, delete and tidy up branches) single / depends on subcommand
gz log Trace the commit history and print the full hash to stdout single
gz cherry-pick Pick commits and cherry-pick them multiple
gz restore Pick files to restore or unstage multiple
gz add Pick unstaged and untracked files and stage them multiple
gz stash <subcommand> Stash changes away, and search stashes to apply or drop them depends on subcommand
gz tag Pick a tag to print, switch to, or diff single
gz reflog Trace the HEAD reflog and recover commits you thought were lost single
gz commit Pick changed files and commit only those multiple
gz fixup Pick the commit to amend and create a fixup commit single
gz merge Pick the branch to merge (recovery menu while one is in progress) single
gz rebase Pick the base to rebase onto (recovery menu while one is in progress) single
gz revert Pick the commits to undo and revert them multiple
gz status List changed files and act on the ones you pick (two-step selection) multiple → single
gz diff Pick what to compare and show the diff depends on mode
gz fetch Decide what to fetch and fetch it (--siblings also fetches the repositories next door; uses the network) single / multiple with --siblings
gz pull Pick branches and bring them up to their upstream in one go (fast-forward only; uses the network) multiple
gz sync Sync the current branch with its upstream (uses the network) none
gz worktree List and manage worktrees single

Build it from source

fuzgit is not published to crates.io yet, so install it from a local clone.

git clone https://github.com/hatohato25/fuzgit.git
cd fuzgit
cargo install --path .

This installs ~/.cargo/bin/gz (the package is fuzgit, the command is gz). To try it without installing:

cargo build --release
./target/release/gz --help

Requirements

  • git must be installed (required). Write operations and coloured diff previews shell out to the system git; gix is used to read repository data.
  • Git 2.38+ for the merge conflict prediction (optional). Older git simply skips the prediction — the merge itself still runs.
  • A stable Rust toolchain to build it (Rust 1.85 or newer, because the crate uses edition 2024).

Read the docs