English | 日本語
A git CLI built around picking, searching, and tracing with a fuzzy finder.
Even when you don't remember the exact branch name or commit hash, you can complete everyday git
operations just by filtering and selecting. Every subcommand follows the same model:
collect candidates → filter and select in a fuzzy finder (with preview) → run the git operation.
The fuzzy finder is skim embedded as a library, so no external
fzf / sk binary is required.
- Package name:
fuzgit - Executable (binary) name:
gz
gitmust be installed on your system (required) Write operations and colored diff generation for previews shell out to the systemgitcommand (gix is used for reading repository information).- Git 2.38 or later for
gz mergeconflict prediction (optional) On older versions only the prediction display is skipped; the merge itself still runs. - A stable Rust toolchain if you build from source (Rust 1.85 or later, since it uses edition 2024)
brew tap hatohato25/fuzgit
brew trust --formula hatohato25/fuzgit/fuzgit
brew install hatohato25/fuzgit/fuzgitThe formula is named fuzgit, but the installed command is gz.
gz --versionPrebuilt binaries are provided for macOS (Apple Silicon / Intel) and Linux (x86_64).
Homebrew is the macOS route; on Linux and WSL use the install script instead.
curl -fsSL https://raw.githubusercontent.com/hatohato25/fuzgit/main/install.sh | shIt downloads the release tarball for your machine, verifies its SHA-256
checksum, and installs gz into ~/.local/bin — no sudo, nothing written
outside that directory. If ~/.local/bin is not on your PATH, the script says
so and prints the line to add; it does not edit your shell profile for you.
Options:
# Install a specific release instead of the latest
curl -fsSL .../install.sh | sh -s -- --version v0.5.0
# Install somewhere else (may need sudo depending on the directory)
curl -fsSL .../install.sh | sh -s -- --bin-dir /usr/local/binFUZGIT_VERSION and FUZGIT_BIN_DIR do the same thing as the two flags.
Only x86_64 is published for Linux today. On aarch64 the script stops and
tells you to build from source rather than installing a binary that cannot run.
Piping a script into a shell means running code you have not read. The script is
install.shin this repository — read it first if you would rather, then run it locally.
The crate is not published on crates.io yet, so clone the repository and install locally.
git clone https://github.com/hatohato25/fuzgit.git
cd fuzgit
cargo install --path .This installs ~/.cargo/bin/gz (package name fuzgit, command name gz).
To try it without installing:
cargo build --release
./target/release/gz --helpgz branch # pick a branch and switch to it
gz status # pick changed files, then add / restore / stash / commit them
git show "$(gz log)" # pick a commit and get its full hash
gz log --action # pick a commit, then pick what to do with itRunning gz with no arguments, or gz --help, lists the subcommands.
gz log and gz reflog print a full hash and stop, which is what makes $(gz log) work.
Add --action and they show a second menu instead — show the commit, switch to it as a
detached HEAD, cherry-pick, revert, create a fixup commit, or (from gz reflog) reset the
current branch back to it after a y/N confirmation. Printing the hash is one of the menu
entries, so you can still get back to the piping workflow.
Without --action nothing changes: the default output is exactly what it has always
been, so git show "$(gz log)" keeps working. gz reflog --restore <NAME> is not deprecated
either — use --restore when the operation needs a name you have to type, and --action
for everything that does not. The two cannot be combined.
Messages, prompts, finder headers, and --help come in English and Japanese. The default is
English; switch to Japanese with:
git config --global fuzgit.lang ja # persistent. --local sets it per repository
gz --lang ja branch # one-off, on any subcommandThe display language is resolved in this order, and the first layer that decides it wins.
| Priority | Source |
|---|---|
| 1 | --lang <ja|en|auto> (global option, available on every subcommand) |
| 2 | FUZGIT_LANG environment variable |
| 3 | git config fuzgit.lang (system / global / local / worktree all apply as usual) |
| 4 | LC_ALL → LC_MESSAGES → LANGUAGE → LANG |
| 5 | fallback: en |
Layers 1-3 are explicit instructions to fuzgit, so any value other than ja / en / auto stops
with an error. Layer 4 only describes the environment, so a value fuzgit cannot interpret
(including C and POSIX) is not an error — resolution just moves on to the fallback. auto
skips the remaining explicit layers and resolves from the environment. fuzgit has no configuration
file of its own; it borrows git's fuzgit.lang key, which is also readable outside a repository.
Two limits are worth knowing:
- Messages from git itself are not guaranteed to be translated. fuzgit tells the git commands
it runs which language to speak, but whether a catalog exists depends on how git was built (NLS)
and on the installed locale data. In particular git upstream ships no Japanese catalog, so
git's own output stays English even when you pick
ja. - Text that clap prints on its own (
Usage:,Options:,Commands:, parser errors) stays in English, because clap 4 has no localization hook. fuzgit's own descriptions in--helpdo switch.
Five of the 18 commands that best show what fuzgit is about.
gz branch — pick a branch and switch to it
You don't need to remember the exact branch name; just filter and select. The preview shows the last
50 commits of the highlighted branch (git log --oneline --decorate). With --all, remote-tracking
branches are included as candidates, and selecting origin/feature creates a tracking local branch
through git's DWIM behavior.
$ gz branch --all
> * main
feature/login
origin/feature/search
gz stash — search stashes and restore them
Candidates for apply / pop / drop are shown as stash@{n}: <message>, so you can filter by
message instead of by number. The preview is git stash show -p --color=always, and drop asks for
confirmation ([y/N]) before running. gz stash push supports multi-select with Tab, and stashes
only the files you picked (unselected changes stay in the working tree).
gz fetch -s — fetch neighboring repositories too
With -s / --siblings, fuzgit scans only the directory directly above the current worktree root
(no recursion) and offers every directory containing a .git as a candidate. Each line is
<directory name> <remote>/<current branch>, and the current repository starts out selected.
Multi-select with Tab to fetch several repositories at once. Repositories that can't be fetched are
not silently dropped — the number excluded is shown in the header.
$ 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
The selected repositories are fetched in parallel — the wait is network round trips, and the
targets are separate repositories. Four run at a time by default; git config fuzgit.fetchJobs <n>
changes that, and 1 restores the fully serial behaviour. The setting applies to
gz fetch --siblings only; plain gz fetch and gz pull fetch once and have nothing
to parallelise.
Each repository's output is captured and printed as one block when it finishes, so the update tables
never interleave. The parallel phase cannot prompt you for anything, so anything that needs a
password or passphrase is run again afterwards, one at a time, with the terminal attached — that
second run is not a retry, just a different way of running it. If you set core.sshCommand in git
config, the parallel phase overrides it and every target falls through to that serial pass; the
result is still correct, you just don't get the speedup.
gz pull — bring several branches up to date at once
The only thing you pick is which local branches should follow their upstream, and integration is fast-forward only. The current branch starts out selected. Branches run one at a time in list order; a failure doesn't abort the run, and the successes and failures are tallied at the end. Branches that can't be targeted (for example, no upstream configured) are reported as an excluded count in the header.
$ gz pull
[1/4] main
[2/4] alpha
[3/4] diverged
[4/4] zeta
3 succeeded / 1 failed (failed: diverged)
Branches run one at a time here, and that is deliberate: the per-remote fetch is effectively a single call (most repositories track one upstream remote), and integrating branches writes to a single repository, where the index and ref locks would collide.
Desktop notification when a long run finishes. Both gz fetch --siblings and gz pull can tell
you when they are done, which helps when you have stepped away.
git config --global fuzgit.notify trueIt is off unless you turn it on, and it stays quiet for runs shorter than ten seconds. The body
is the count only — no repository, branch or path names. Whether a banner actually appears depends on
your environment (macOS uses osascript and needs notifications allowed for your terminal; Linux
uses notify-send, which may not be installed). fuzgit never treats that as a failure, and the
tally is always written to stderr regardless, so the notification is a convenience, never the only
way you learn the result.
| Subcommand | Description |
|---|---|
gz branch |
Pick a branch and switch to it (subcommands also create, delete, and tidy up) |
gz log |
Trace commit history and print the full hash to stdout (--action to pick what to do next) |
gz cherry-pick |
Pick a commit and cherry-pick it |
gz restore |
Pick files to restore or unstage |
gz add |
Pick unstaged and untracked files to stage |
gz stash <subcommand> |
Stash changes, then search stashes to apply or drop them |
gz reflog |
Trace the HEAD reflog and recover lost commits (--action to pick what to do next) |
gz commit |
Pick changed files and commit only those |
gz fixup |
Pick the commit to amend and create a fixup commit |
gz merge |
Pick a branch to merge (resume menu while one is in progress) |
gz rebase |
Pick the rebase base (resume menu while one is in progress) |
gz revert |
Pick a commit to revert |
gz status |
List changed files and act on the ones you pick (two-step selection) |
gz diff |
Pick what to compare and show the diff |
gz fetch |
Choose what to fetch (--siblings fetches neighboring repositories too. uses the network) |
gz pull |
Pick branches and bring them up to their upstream at once (fast-forward only. uses the network) |
gz worktree |
List and manage worktrees (add <name> creates it next to the repository and copies .claude/ into it) |
gz pr |
Pick a GitHub pull request and check it out (--worktree <name> opens it in a review worktree. needs the gh CLI. uses the network) |
Options, how candidates are built, what the preview shows, and whether a confirmation prompt appears are all described in the documentation.
gz pr — pick a pull request and check it out
The one command that reaches outside git. It lists your open pull requests, you filter and
pick one, and it runs gh pr checkout. What you end up with is ordinary local git state: a
branch, its tracking config, and a checkout.
$ gz pr
Fetching the open pull requests from GitHub
> #142 fix/login-redirect octocat Fix the redirect after login
#139 feat/search-filters contributor Add filters to the search form
#131 chore/bump-deps app/dependabot chore(deps): bump the actions group
The preview is the pull request body, and it is already in memory — the candidate list is
fetched once with the body riding along, so moving the cursor never touches the network.
That is the whole reason this command fits fuzgit: everywhere else, candidate lists and
previews are built from local data only, and gz pr breaks that rule exactly once.
--checks adds the review decision and the CI status to each line. It is off by default
because it is slow: measured against a repository with 30 open PRs, the default fetch takes
about 1.1s while --checks takes roughly three times that.
--action opens a second menu after you pick — show the pull request, show the diff, print
the number, print the URL — the same shape as gz log --action.
gz pr --worktree <name> opens the pull request in its own worktree, next to the
repository root, and then does what gz worktree add does afterwards: copies your
gitignored .claude/ across and installs dependencies from the lockfile it finds. Reviewing
a pull request no longer disturbs the tree you are working in.
gz pr --worktree review-142 # check PR out into ../review-142, install deps
gz pr --worktree review-142 --no-install # skip the dependency installgh is not a required dependency. fuzgit still assumes only git. Without gh,
gz pr stops with a message pointing at https://cli.github.com and every other command
keeps working. One thing to know: gh speaks English only, so its lines stay English
even with --lang ja — the same limitation as git's own messages.
After every change, make sure all of the following succeed, in this order.
cargo build
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test- Testing policy, design notes, and module layout: documentation
- The documentation site's source lives in
docs/(GitHub Pages publishesdocs/from themainbranch)
MIT License. See LICENSE for the full text.