fuzgit

fuzgit documentationfuzgit ドキュメント

fuzgit is a git CLI built around picking, searching and tracing with a fuzzy finder. The goal is to let you finish everyday git work without remembering branch names or commit hashes exactly: you narrow the list down and pick.

Every subcommand follows the same model — build the candidate list → filter and pick it in the fuzzy finder (with a preview) → run the git operation. The fuzzy finder is skim embedded as a library, so no external fzf or sk binary is needed.

  • Package name: fuzgit
  • Command (binary) name: gz

fuzgit speaks English and Japanese, English being the default (see Language). Console excerpts on this page show the default English display.

fuzzy finder で「選ぶ」「探す」「辿る」ことを軸にした git 操作 CLI ツールです。

ブランチ名やコミットハッシュを正確に覚えていなくても、絞り込みと選択だけで日常的な git 操作を完結できることを目指しています。すべてのサブコマンドが 候補一覧の取得 → fuzzy finder で絞り込み・選択(+プレビュー)→ git 操作の実行という同じ操作モデルに従います。fuzzy finder は skim をライブラリとして組み込んでいるため、 外部の fzf / sk バイナリは不要です。

  • パッケージ名: fuzgit
  • 実行コマンド名(バイナリ名): gz

Requirements前提条件 #

  • git must be installed on the system (required). Write operations (switch / cherry-pick / restore / add / stash and so on) and the coloured diffs used for previews are executed by shelling out to the system git command. Repository data is read with gix, but gix does not provide those write operations. If git is not on PATH, fuzgit reports “git was not found. Install git and make sure it is on your PATH” and exits.
  • Git 2.38 or newer is needed for the conflict prediction of gz merge (optional). The prediction uses git merge-tree --write-tree. Older git rejects that option, so only the prediction is skipped and the merge still runs (the confirmation prompt says “Conflict prediction: skipped (`git merge-tree --write-tree` could not be run; the prediction needs Git 2.38 or later)”). It never stops with an error. No other feature has a minimum git version.
  • A stable Rust toolchain to build from source (Rust 1.85 or newer because the crate uses edition 2024; development is verified with 1.95.0).
  • システムに git がインストールされていること(必須)
    書き込み系の操作(switch / cherry-pick / restore / add / stash など)と プレビュー用の色付き差分生成は、システムの git コマンドへ シェルアウトして実行します。リポジトリ情報の読み取りには gix を使いますが、gix は 上記の書き込み操作を提供していないためです。git が PATH 上に 無い場合は「git コマンドが見つかりません」と表示して終了します。
  • gz merge のコンフリクト予測には Git 2.38 以降が必要です(任意)
    予測には git merge-tree --write-tree を使います。2.38 未満の git では このオプションが拒否されるため、予測の表示だけを省略して merge の実行は そのまま続けます(確認プロンプトに「コンフリクト予測: 省略しました」と 表示されます)。エラーで停止することはありません。これ以外の機能に git の 最低バージョン要件はありません。
  • ビルドする場合は stable の Rust ツールチェイン(edition 2024 を使うため Rust 1.85 以降。開発時の確認は 1.95.0)

Installationインストール #

fuzgit is not published to crates.io, so clone the repository and install from the local checkout.

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

crates.io へは未公開のため、リポジトリを取得してローカルからインストールします。

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

~/.cargo/bin/gz がインストールされます(パッケージ名は fuzgit、コマンド名は gz)。

インストールせずに試す場合:

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

Usage使い方 #

gz <subcommand> [options]

Running gz with no arguments, or gz --help, lists the subcommands.

引数なしの gz および gz --help で サブコマンド一覧を表示します。

An option gets a short form only when git itself spells it the same waygz restore -S (git restore -S), gz fetch -p (git fetch -p), gz sync -r (git pull -r), gz branch delete -f (git branch -f) and so on. fuzgit-specific options are long-only, and a letter git already uses for something else is never reused: gz tag --diff has no -d because git tag -d deletes a tag.

オプションの短縮形は、git 本体に同じ意味の綴りがあるものだけに 付けていますgz restore -S = git restore -Sgz fetch -p = git fetch -pgz sync -r = git pull -rgz branch delete -f = git branch -f など)。fuzgit 固有のオプションは長い綴りだけとし、 git が別の意味で使っている文字は流用しません。gz tag --diff-d が無いのは、git tag -d がタグの削除だからです。

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 コミットを選んで cherry-pick する 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 変更を stash へ退避し、stash を検索して適用・破棄する depends on subcommand サブコマンド次第
gz tag Pick a tag to print, switch to, or diff タグを選んで出力・切替・差分表示する single単一
gz reflog Trace the HEAD reflog and recover lost commits HEAD の reflog を辿り、失われたコミットを取り出す single単一
gz commit Pick changed files and commit only those 変更ファイルを選んで、選んだものだけをコミットする multiple複数
gz fixup Pick the commit to amend and create a fixup commit 修正対象のコミットを選んで fixup コミットを作る single単一
gz merge Pick the branch to merge (recovery menu while one is in progress) merge するブランチを選ぶ(進行中は復帰メニュー) single単一
gz rebase Pick the base to rebase onto (recovery menu while one is in progress) rebase の base を選ぶ(進行中は復帰メニュー) single単一
gz revert Pick the commits to undo and revert them 打ち消すコミットを選んで revert する multiple複数
gz status List changed files and act on the ones you pick (two-step selection) 変更ファイルを一覧し、選んだファイルに操作を行う(2 段選択) 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) fetch の対象を決めて取得する(--siblings で隣のリポジトリも一括取得。ネットワークを使う single / multiple with --siblings 単一/--siblings は複数
gz pull Pick branches and bring them up to their upstream in one go (fast-forward only; uses the network) ブランチを選んで upstream へまとめて追随させる(fast-forward のみ。ネットワークを使う multiple複数
gz sync Sync the current branch with its upstream (uses the network) 現在ブランチを upstream と同期する(ネットワークを使う none選択なし
gz worktree List and manage worktrees worktree を一覧・管理する single単一

Language表示言語 #

Every piece of text fuzgit produces — messages, errors, finder headers, confirmation prompts and --help — is available in English and Japanese. English is the default. To use Japanese:

git config --global fuzgit.lang ja   # persistent (--local sets it per repository)
gz --lang ja branch                  # one-off, accepted by every subcommand

fuzgit が表示するすべての文言(メッセージ・エラー・finder のヘッダー・ 確認プロンプト・--help)が日本語と英語に 対応しています。既定は英語です。日本語にするには次のように 指定します。

git config --global fuzgit.lang ja   # 永続設定(--local ならリポジトリごと)
gz --lang ja branch                  # 単発の上書き。全サブコマンドで指定できる
The first layer that decides the language wins; the layers below it are not consulted. 先に決まった層より下は参照しません。
Priority優先 Source取得元 Meant for用途
1 --lang <ja|en|auto> A one-off override. A global option, accepted by every subcommand; there is no short form. 単発の上書き。全サブコマンド共通のグローバルオプションで、短縮形はありません。
2 FUZGIT_LANG Per shell or session. シェル・セッション単位の上書き。
3 git config fuzgit.lang The persistent setting. system / global / local / worktree all apply exactly as they do for git itself. 永続設定。system / global / local / worktree の階層が git 本体と同じように効きます。
4 LC_ALLLC_MESSAGESLANGUAGELANG Read from the environment, in the POSIX and gettext order. 環境からの自動判定(POSIX / gettext と同じ順)。
5 fallbackフォールバック en
  • Layers 1–3 are strict. They are explicit instructions to fuzgit, so any value other than ja, en or auto stops with an error instead of quietly falling back. An empty value counts as “not set” and resolution moves to the next layer. If reading fuzgit.lang fails altogether, fuzgit stops as well.
  • Layer 4 is lenient. Locale variables describe the environment rather than instruct fuzgit, so a value it cannot interpret — C and POSIX included — is not an error; it simply means “not Japanese” and the fallback applies.
  • auto means “skip the remaining explicit layers and read the environment”, wherever it appears in layers 1–3.
  • There is no fuzgit configuration file. The setting lives in git’s own fuzgit.lang key, read in-process with gix — no git process is started to resolve the language.
  • The language resolves outside a repository too, so gz --help and the “not a git repository” error are translated as well; system and global settings still apply there. One exception: a fuzgit.lang written under a conditional include (includeIf) in ~/.gitconfig is not read when resolving outside a repository.

Two limits are worth knowing about.

  • Messages from git itself are not guaranteed to be translated. fuzgit tells every git command 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. Forcing en is the safe direction, because it only asks git for its original text.
  • Text that clap prints on its own stays EnglishUsage:, Options:, Commands: and parser errors such as error: unexpected argument. clap 4 has no localization hook. fuzgit’s own descriptions in --help, including the one for --lang, do switch.

Output that fuzgit parses is a separate matter: those git invocations always run with LC_MESSAGES=C, whatever display language was resolved, so parsing never depends on the environment. See Debugging for how the two kinds are told apart in the log.

  • 層 1〜3 は厳格です。fuzgit への明示的な指定であるため、 ja / en / auto 以外の値は 黙って既定へ倒さずエラーで停止します。空文字は「未設定」として扱い、 次の層へ進みます。fuzgit.lang の読み取り自体に失敗した場合も 停止します。
  • 層 4 は寛容です。ロケール環境変数は fuzgit への指示ではなく 環境の記述であるため、解釈できない値(C / POSIX を 含む)はエラーにせず「日本語ではない」と判定してフォールバックへ進みます。
  • auto は層 1〜3 のどこに現れても「以降の明示指定を飛ばして 環境からの自動判定へ進む」という意味です。
  • fuzgit 独自の設定ファイルはありません。設定は git config の fuzgit.lang に置き、gix でプロセス内から読みます (言語の解決で git プロセスは起動しません)。
  • 言語はリポジトリの外でも解決できますgz --help や「git リポジトリではありません」のエラーも 翻訳対象で、system / global の設定はそのまま効きます。ただし ~/.gitconfig の条件付き include(includeIf)配下に 書いた fuzgit.lang は、リポジトリ外での解決では読まれません。

以下の 2 点は制約として把握しておいてください。

  • git 本体のメッセージが翻訳されることは保証しません。 fuzgit は起動するすべての git へ言語を伝えますが、翻訳が存在するかは git のビルド(NLS の有無)とロケールデータに依存します。とくに git 本体には日本語カタログがありませんja を 選んでも git 自身の出力は英語のままです。en への統一は git に原文を出させるだけなので、環境に依存せず成立します。
  • clap が自前で出す固定文言は英語のままです。 Usage: / Options: / Commands:error: unexpected argument のようなパーサエラーが該当します (clap 4 に多言語化の機構がないため)。--lang の説明を含め、 --help のうち fuzgit 自身の文言は切り替わります。

fuzgit がパースする git の出力は別扱いで、解決された表示言語に 関わらず常に LC_MESSAGES=C で実行します(実行環境によって パースが壊れないようにするため)。ログ上での見分け方は デバッグを参照してください。

gz branch — switch, create, delete and tidy up branches gz branch — ブランチの切替・作成・削除・整理 #

gz branch                  # pick a local branch and run git switch (the classic behaviour)
gz branch --all            # also list remote-tracking branches (origin/... and friends)
gz branch create <name>    # pick the starting point and create a new branch
gz branch delete           # pick branches and delete them
gz branch cleanup          # delete every merged branch in one go

gz branch with no arguments and gz branch --all are the branch switch, exactly as before. create / delete / cleanup are management operations added later, and combining the switch flag (-a / --all) with a management subcommand is rejected by clap, because it would be ambiguous which operation was intended (gz branch --all create x is an error).

Switching (no subcommand)

OptionDescription
-a, --allAlso list remote-tracking branches
  • The current branch is marked with a leading *, just like git branch.
  • The preview shows the 50 most recent commits of the highlighted branch (git log --oneline --decorate).
  • Picking a remote-tracking branch (origin/feature) runs git switch with the short name (feature), so git’s DWIM creates the tracking local branch.

gz branch create <name> — creating a branch

Argument / optionDescription
<NAME>Name of the branch to create (required positional argument)
--switchSwitch to the branch after creating it
  • What you pick in the fuzzy finder is the starting point. Candidates are local branches, remote-tracking branches and tags, each line prefixed with its kind (branch / tag), because a branch and a tag can share a name.
  • It runs git branch -- <name> <start point>. For an annotated tag the resolved ID is passed and git peels it to the commit.
  • The branch name is a positional argument (skim has no text input UI — see Out of scope). Name validation is left to git’s check-ref-format; names starting with - are protected with -- and then rejected by git.
  • Without --switch the branch is only created, and the command to switch to it is printed to stderr.

gz branch delete — deleting branches

OptionDescription
-f, --forceDelete branches that are not merged either (git branch -D)
--into <BRANCH>Branch used as the merged/unmerged baseline (defaults to HEAD)
  • Candidates are local branches except the current branch and branches checked out in another worktree (git refuses to delete either).
  • A candidate line is <name> merged|unmerged <relative update time> tracking: <upstream>|no tracking branch. Only information that can be gathered for every branch at once is put on the line; details of the last commit are in the preview (git log --oneline).
  • Tab selects several branches. A confirmation prompt ([y/N]) lists every target before anything runs.
  • The default is git branch -d (merged branches only). If even one unmerged branch is in the selection, fuzgit stops with a dedicated error before running git branch, so it never deletes part of the selection and then stops.
  • Only --force switches to git branch -D, and the confirmation prompt then warns about the unmerged branches.

gz branch cleanup — deleting merged branches in one go

OptionDescription
--into <BRANCH>Branch used as the merged baseline (defaults to HEAD)
  • Candidates are limited to merged branches, and all of them start out selected (use Tab to deselect).
  • After the confirmation prompt they are deleted with git branch -d. There is no --force — handling unmerged branches is the job of gz branch delete --force.
gz branch                  # ローカルブランチから選んで git switch(従来どおりの切替)
gz branch --all            # リモート追跡ブランチ(origin/... 等)も候補に含める
gz branch create <name>    # 作成元を選んで新しいブランチを作る
gz branch delete           # ブランチを選んで削除する
gz branch cleanup          # merged なブランチを一括で削除する

引数なしの gz branchgz branch --all は 従来どおりのブランチ切替です。 create / delete / cleanup は 後から追加した管理操作で、切替用のフラグ(-a / --all)と 管理サブコマンドの併用は、どちらの操作を意図したのかが曖昧になるため clap の段階で 拒否されます(gz branch --all create x はエラー)。

切替(サブコマンドなし)

オプション説明
-a, --allリモート追跡ブランチも候補に含める
  • 現在のブランチは git branch と同じく行頭の * で識別できます。
  • プレビューには選択中ブランチの直近 50 件のコミットログ(git log --oneline --decorate)を表示します。
  • リモート追跡ブランチ(origin/feature)を選ぶと短縮名(feature)で git switch するため、git の DWIM により追跡ローカルブランチが作成されます。

gz branch create <name> — ブランチの作成

引数・オプション説明
<NAME>作成するブランチ名(必須の位置引数)
--switch作成後にそのブランチへ切り替える
  • fuzzy finder で選ぶのは作成元です。候補はローカルブランチ・ リモート追跡ブランチ・タグで、行頭に種別(branch / tag)が 付きます(同名のブランチとタグが共存できるため)。
  • 実行するのは git branch -- <name> <作成元> です。 annotated tag を選んだ場合は解決済みの ID を渡し、git が対象コミットまで peel します。
  • ブランチ名は位置引数です(skim にテキスト入力 UI が無いため。 スコープ外・非対応参照)。名前の妥当性検証は git の check-ref-format に委ねており、- 始まりの名前は -- で保護したうえで git が拒否します。
  • --switch を付けない場合は作成のみを行い、切り替えるためのコマンドを 標準エラーへ案内します。

gz branch delete — ブランチの削除

オプション説明
-f, --forcemerged でないブランチも削除する(git branch -D
--into <BRANCH>merged 判定の基準ブランチ(既定は HEAD)
  • 候補はローカルブランチのうち、現在のブランチと、他の worktree で チェックアウト中のブランチを除いたものです(どちらも git が削除を許可しないため)。
  • 候補行は <名前> merged|unmerged <相対更新日時> 追跡: <upstream>|追跡なし です。 いずれも全件を一括取得できる情報に限っており、最終コミットの詳細はプレビュー (git log --oneline)で確認します。
  • Tab で複数選択できます。 実行前に確認プロンプト([y/N])で対象を全件列挙します。
  • 既定は git branch -d(merged のみ削除可)です。 unmerged が選択に 1 件でも含まれると、git branch を実行する前に 専用エラーで停止します(一部だけ削除して止まることはありません)。
  • --force を指定したときだけ git branch -D を使い、 確認プロンプトに unmerged である旨の警告を含めます。

gz branch cleanup — merged なブランチの一括削除

オプション説明
--into <BRANCH>merged 判定の基準ブランチ(既定は HEAD)
  • 候補を merged のブランチのみに絞り、全件を起動時から選択済みで 表示します(Tab で外せます)。
  • 確認プロンプトを経て git branch -d で一括削除します。 --force はありません(unmerged を扱うのは gz branch delete --force の役目です)。

gz log — exploring the commit history gz log — コミット履歴の探索 #

gz log                 # 1000 commits by default
gz log --limit 200     # limit how many commits are listed (-n also works)
git show "$(gz log)"   # use the full hash of the picked commit directly
OptionDefaultDescription
-n, --limit <N>1000Maximum number of commits to list
  • Each candidate is one line, <short hash> <date> <summary> (<author>), and that whole string is what you filter on.
  • The preview is git show --color=always (commit details and diff).
  • On confirmation only the full hash of the picked commit goes to stdout, for pipes and copying. Messages go to stderr, so $(gz log) is safe.
gz log                 # 既定 1000 件を候補にする
gz log --limit 200     # 取得件数を制限する(-n でも可)
git show "$(gz log)"   # 選んだコミットのフルハッシュをそのまま使う
オプション既定値説明
-n, --limit <N>1000取得するコミットの最大件数
  • 候補は <短縮ハッシュ> <日付> <サマリ> (<作者>) の 1 行で、この文字列がそのまま絞り込み対象です。
  • プレビューは git show --color=always(コミットの詳細と差分)。
  • 決定すると選択コミットのフルハッシュだけを標準出力へ出します (パイプ・コピー用途)。メッセージ類は標準エラーへ出すため、$(gz log) で 安全に受け取れます。

gz cherry-pick — picking commits to cherry-pick gz cherry-pick — cherry-pick するコミットの選択 #

gz cherry-pick                     # commits from every branch
gz cherry-pick --branch feature    # restrict to one branch
OptionDescription
-b, --branch <BRANCH>Target branch (without it, commits from all branches are listed)
  • Tab selects several commits. Whatever order you picked them in, they are always cherry-picked oldest first (history order).
  • The candidate limit is 1000, the same as the default of gz log --limit.
  • On a conflict, git’s message is shown as-is and the exit code is non-zero. Continue or abort with git cherry-pick --continue / git cherry-pick --abort.
gz cherry-pick                     # 全ブランチのコミットを候補にする
gz cherry-pick --branch feature    # 対象ブランチを指定する
オプション説明
-b, --branch <BRANCH>対象ブランチ(未指定時は全ブランチのコミットを候補にする)
  • Tab複数選択できます。選んだ順に関わらず、常に古い順(履歴順)に cherry-pick します。
  • 候補件数の上限は gz log --limit の既定値と同じ 1000 件です。
  • コンフリクト時は git のメッセージをそのまま表示して非ゼロ終了します。 続行・中止は git cherry-pick --continue / git cherry-pick --abort で行ってください。

gz restore — picking files to restore or unstage gz restore — 復元・アンステージするファイルの選択 #

gz restore                       # discard working tree changes (with confirmation)
gz restore --staged              # unstage staged changes
gz restore --source HEAD~1       # overwrite with the content of a revision (with confirmation)
OptionDescription
-s, --source <REV>Revision to restore from; the file list of that commit becomes the candidate list
-S, --stagedUnstage staged changes
  • Tab selects several files. Candidates carry the same status codes as git status. The two status characters are left-aligned: a change that only exists in the working tree reads where git writes  M, and the column stays two characters wide so the paths still line up. They are coloured the way git colours them: green for the index (staged) column, red for the working-tree column, and red for both characters of an untracked file (??). Because of the left alignment, the colour is the only thing that tells a staged change from an unstaged one — where colour is unavailable (NO_COLOR, a pipe) the two look alike.
  • The preview is the diff of the file (with --staged, the diff between HEAD and the index).
  • Anything that rewrites the working tree shows a confirmation prompt ([y/N]) first. Unstaging with --staged does not damage the working tree, so it is not confirmed.
gz restore                       # 作業ツリーの変更を破棄する(確認あり)
gz restore --staged              # ステージ済みの変更をアンステージする
gz restore --source HEAD~1       # 指定リビジョンの内容で上書きする(確認あり)
オプション説明
-s, --source <REV>復元元のリビジョン。指定するとそのコミットのファイル一覧が候補になる
-S, --stagedステージ済みの変更をアンステージする
  • Tab で複数選択できます。候補には git status と 同じ状態コードが付きます。状態コードの 2 文字は左詰めで表示します(作業ツリーだけの変更は git の  M ではなく 。列幅は 2 文字のままなのでパスの開始位置は揃います)。配色は git と同じで、index(staged)列は緑、作業ツリー列は赤、未追跡(??)は 2 文字とも赤です。左詰めにしたため、staged と unstaged の区別は色だけが担いますNO_COLOR やパイプ経由など色の付かない環境では両者が同じ見た目になります)。
  • プレビューは対象ファイルの差分(--staged 指定時は HEAD とインデックスの差分)。
  • 作業ツリーを書き換える操作は実行前に確認プロンプト([y/N])を 表示します。--staged によるアンステージは作業ツリーを 壊さないため確認しません。

gz add — picking files to stage gz add — ステージするファイルの選択 #

gz add
  • Candidates are unstaged changes and untracked files (files that are already staged with no remaining difference do not appear), each line prefixed with the same status code as git status. The two status characters are left-aligned: a change that only exists in the working tree reads where git writes  M, and the column stays two characters wide so the paths still line up. They are coloured the way git colours them: green for the index (staged) column, red for the working-tree column, and red for both characters of an untracked file (??). Because of the left alignment, the colour is the only thing that tells a staged change from an unstaged one — where colour is unavailable (NO_COLOR, a pipe) the two look alike.
  • Tab selects several files.
  • The preview is the diff for tracked files, and the file content (up to the first 64 KiB) for untracked files.
  • 候補は未ステージの変更と未追跡ファイル(既にステージ済みで差分の無いファイルは 出ません)で、行頭には git status と同じ状態コードが付きます。状態コードの 2 文字は左詰めで表示します(作業ツリーだけの変更は git の  M ではなく 。列幅は 2 文字のままなのでパスの開始位置は揃います)。配色は git と同じで、index(staged)列は緑、作業ツリー列は赤、未追跡(??)は 2 文字とも赤です。左詰めにしたため、staged と unstaged の区別は色だけが担いますNO_COLOR やパイプ経由など色の付かない環境では両者が同じ見た目になります)。
  • Tab で複数選択できます。
  • プレビューは追跡ファイルは差分、未追跡ファイルはファイルの内容(先頭 64KiB まで)。

gz stash — creating, searching and restoring stashes gz stash — stash の作成・検索・復元 #

push picks files in the working tree, while apply / pop / drop pick an existing stash. Because the thing being picked differs, they are separate subcommands rather than flags. Bare gz stash prints the subcommand help — it never silently assumes one of them.

gz stash push                      # pick from the tracked changes and stash them
gz stash push -m "work in progress"  # add a message
gz stash push -u                   # also list untracked files
gz stash apply                     # apply the picked stash (the stash stays)
gz stash pop                       # apply it and drop that stash
gz stash drop                      # discard the picked stash (with confirmation)
SubcommandOptionDescription
push-m, --message <MESSAGE>Message attached to the stash
push-u, --include-untrackedAlso list untracked files (by default only tracked changes)
apply / pop / drop(none)Pick one entry from the stash list
  • push supports multi-select with Tab, and only the files you picked are stashed (the rest stay in the working tree). Staged changes are stashed too, so the preview is the diff against HEAD (git diff HEAD).
  • Candidates for apply / pop / drop are stash@{n}: <message>, so you can filter by message. The preview is git stash show -p --color=always.
  • drop cannot be undone, so it shows a confirmation prompt ([y/N]) first.

push が選ぶのは作業ツリーの「ファイル」、apply / pop / drop が選ぶのは既存の「stash」です。 選択対象が異なるため、フラグではなくサブコマンドで分けています。 引数なしの gz stash はサブコマンド一覧のヘルプを表示します (どちらかへ暗黙に倒しません)。

gz stash push                      # 追跡済みの変更から選んで退避する
gz stash push -m "作業中"          # メッセージを付ける
gz stash push -u                   # 未追跡ファイルも候補に含める
gz stash apply                     # 選んだ stash を適用する(stash は残る)
gz stash pop                       # 適用して、その stash を取り除く
gz stash drop                      # 選んだ stash を破棄する(確認あり)
サブコマンドオプション説明
push-m, --message <MESSAGE>stash に付けるメッセージ
push-u, --include-untracked未追跡ファイルも候補に含める(既定は追跡済みの変更のみ)
apply / pop / drop(なし)stash 一覧から 1 件選ぶ
  • pushTab で複数選択でき、選んだファイルだけが 退避されます(選ばなかった変更は作業ツリーに残ります)。ステージ済みの変更も 退避対象になるため、プレビューは HEAD との差分(git diff HEAD)です。
  • apply / pop / drop の候補は stash@{n}: <メッセージ> で、メッセージで絞り込めます。 プレビューは git stash show -p --color=always
  • drop は元に戻せないため、実行前に確認プロンプト([y/N])を表示します。

gz tag — picking a tag gz tag — タグの選択 #

gz tag                  # print the tag name to stdout (default)
gz tag --switch         # switch to the picked tag as a detached HEAD
gz tag --diff           # show the diff between the picked tag and HEAD
OptionDescription
--switchSwitch to the picked tag as a detached HEAD
--diffShow the diff between the picked tag and HEAD
  • --switch and --diff cannot be combined.
  • Annotated tags show their tag message on the candidate line and in the preview.
  • By default only the tag name is printed to stdout.
gz tag                  # タグ名を標準出力へ出す(既定)
gz tag --switch         # 選んだタグへ detached HEAD で切り替える
gz tag --diff           # 選んだタグと HEAD の差分を表示する
オプション説明
--switch選択したタグへ detached HEAD で切り替える
--diff選択したタグと HEAD の差分を表示する
  • --switch--diff は同時に指定できません。
  • annotated tag は候補行にタグメッセージを併記し、プレビューにもタグメッセージを含めます。
  • 既定ではタグ名だけを標準出力へ出します。

gz reflog — investigating deleted branches gz reflog — 削除済みブランチの調査 #

gz reflog                            # print the commit hash of the picked entry to stdout
gz reflog --restore recovered        # create a new branch `recovered` from the picked commit
OptionDescription
--restore <NAME>Create a new branch with the given name from the picked commit
  • Candidates are <short hash> HEAD@{n}: <message>, so you can filter on messages such as checkout: moving from ....
  • By default only the full hash goes to stdout. The result of --restore goes to stderr, leaving stdout free for pipes.
gz reflog                            # 選んだエントリのコミットハッシュを標準出力へ出す
gz reflog --restore recovered        # 選んだコミットから新規ブランチ `recovered` を作成する
オプション説明
--restore <NAME>選択したコミットから指定名の新規ブランチを作成する
  • 候補は <短縮ハッシュ> HEAD@{n}: <メッセージ> で、checkout: moving from ... 等のメッセージで絞り込めます。
  • 既定ではフルハッシュだけを標準出力へ出します。--restore の 実行結果は標準エラーへ出るため、標準出力はパイプ用途のまま空けてあります。

gz commit — picking the files to commit gz commit — コミットするファイルの選択 #

gz commit                      # commit only the files you picked (message via the editor)
gz commit -m "fix authentication"   # give the message directly (no editor)
OptionDescription
-m, --message <MESSAGE>Commit message (without it, git opens the editor)
  • Candidates are staged, unstaged and untracked changes in one single list, each line prefixed with the same status code as git status. The two status characters are left-aligned: a change that only exists in the working tree reads where git writes  M, and the column stays two characters wide so the paths still line up. They are coloured the way git colours them: green for the index (staged) column, red for the working-tree column, and red for both characters of an untracked file (??). Because of the left alignment, the colour is the only thing that tells a staged change from an unstaged one — where colour is unavailable (NO_COLOR, a pipe) the two look alike.
  • Files with staged changes start out selected. Tab deselects them, and a deselected entry never comes back when you keep typing a filter.
  • It runs a path-limited commit, git commit -- <paths>.... Therefore changes you did not pick are not committed even if they were staged; they simply stay staged.
  • Untracked files you picked are git added first, because a path-limited commit cannot target them.
  • The preview is the diff against HEAD (git diff --color=always HEAD -- <path>; file content for untracked files). A path-limited commit records the working tree content, so the preview shows the difference from HEAD rather than from the index.
  • With no changes at all it prints “There are no candidates to select from” and exits.

A note about your editor

Without -m, entering the message is left to git commit (that is, EDITOR / GIT_EDITOR / core.editor). If a GUI editor such as VS Code is configured without waiting for the window to close, the editor exits immediately, git decides the message is empty, and the commit is aborted with Aborting commit due to empty commit message. (Plain git commit behaves the same way.) Add the option that makes it wait:

git config --global core.editor "code --wait"   # example for VS Code

Passing -m avoids the problem entirely because no editor is started. When a commit fails, fuzgit prints the above as a hint on stderr.

gz commit                      # 選んだファイルだけをコミットする(メッセージはエディタで入力)
gz commit -m "認証を修正する"   # メッセージを直接指定する(エディタを起動しない)
オプション説明
-m, --message <MESSAGE>コミットメッセージ(省略時は git がエディタを起動する)
  • 候補はステージ済み・未ステージ・未追跡の変更を1 つの一覧にしたもので、 行頭には git status と同じ状態コードが付きます。状態コードの 2 文字は左詰めで表示します(作業ツリーだけの変更は git の  M ではなく 。列幅は 2 文字のままなのでパスの開始位置は揃います)。配色は git と同じで、index(staged)列は緑、作業ツリー列は赤、未追跡(??)は 2 文字とも赤です。左詰めにしたため、staged と unstaged の区別は色だけが担いますNO_COLOR やパイプ経由など色の付かない環境では両者が同じ見た目になります)。
  • ステージ済みの変更があるファイルは起動時から選択済みです。 Tab で選択を外せます(外した選択が絞り込みのたびに復活することはありません)。
  • 実行するのは git commit -- <paths>... のパス指定コミットです。 そのため選ばなかった変更はステージ済みであってもコミットされず、 ステージ状態のまま残ります。
  • 選んだ未追跡ファイルは、パス指定コミットの対象にできないため先に git add します。
  • プレビューは HEAD との差分(git diff --color=always HEAD -- <path>。 未追跡ファイルは内容)。パス指定コミットが記録するのは作業ツリーの内容であるため、 index との差分ではなく HEAD との差分を表示します。
  • 変更が 1 件も無い場合は「選択できる候補がありません」と表示して終了します。

エディタの設定に関する注意

-m を省略した場合、コミットメッセージの入力は git commit (= EDITOR / GIT_EDITOR / core.editor)に 委ねられます。VS Code のような GUI エディタを終了待ちなしで設定していると、 エディタが即座に終了するため git はメッセージが空だと判断し、 Aborting commit due to empty commit message. でコミットが 中止されます(これは素の git commit でも同じです)。 終了を待つオプションを付けてください。

git config --global core.editor "code --wait"   # VS Code の例

-m でメッセージを指定すればエディタを起動しないため、この問題を 回避できます。コミットが失敗したときは、上記の内容をヒントとして標準エラーへ表示します。

gz fixup — picking the target of a fixup commit gz fixup — fixup コミットの対象選択 #

gz fixup            # git commit --fixup=<picked commit>
gz fixup --squash   # git commit --squash=<picked commit>
OptionDescription
--squashCreate a squash commit (messages are combined) instead of a fixup commit
  • What gets committed is the staged changes. With nothing staged, fuzgit prints “There are no staged changes. A fixup commit needs staged changes, so stage them with `gz add` and run this command again” and exits before starting the finder.
  • Candidates are commits reachable from HEAD (1000 by default, shared with gz log --limit). The preview is git show --color=always.
  • Afterwards the steps to fold the commit into history are printed to stderr. The rebase is not run automatically — rewriting history stays an explicit user action.
    Hint: run the following to fold the new commit into the history:
      git rebase -i --autosquash <full hash of the picked commit>^
    If the picked commit is the very first one (no parent), <hash>^ cannot be resolved, so the starting point becomes --root and the reason is printed with it.
  • --squash makes git open the editor to append the message body (--fixup does not).
gz fixup            # git commit --fixup=<選んだコミット>
gz fixup --squash   # git commit --squash=<選んだコミット>
オプション説明
--squashfixup ではなく squash コミット(メッセージを結合する)を作成する
  • コミット対象はステージ済みの変更です。ステージ済みの変更が無い場合は、 finder を起動する前に「ステージ済みの変更がありません」と表示して終了します。
  • 候補は HEAD からのコミット(既定 1000 件、gz log --limit の既定値と共通)。 プレビューは git show --color=always
  • 実行後、履歴へ取り込むための手順を標準エラーへ表示します。 rebase は自動実行しません(履歴改変はユーザーの明示操作に委ねます)。
    ヒント: 作成したコミットを履歴へ取り込むには次を実行してください。
      git rebase -i --autosquash <選んだコミットのフルハッシュ>^
    選んだコミットが最初のコミット(親が無い)の場合は <hash>^ を 解決できないため、起点が --root になります(理由も併記します)。
  • --squash は git がメッセージ本文の追記のためにエディタを起動します(--fixup は起動しません)。

gz merge — picking the branch to merge gz merge — merge するブランチの選択 #

gz merge             # a normal merge (fast-forwards when it can)
gz merge --no-ff     # always create a merge commit
gz merge --squash    # only apply the result to the working tree and index, without committing
gz merge --ff-only   # merge only when it can fast-forward
OptionDescription
--no-ffCreate a merge commit even when a fast-forward is possible
--squashApply the merge result to the working tree and index without committing
--ff-onlyMerge only when a fast-forward is possible
  • The three options are mutually exclusive (combining them is rejected before the selection starts).
  • The merge destination is fixed to the current position (HEAD); candidates are local and remote-tracking branches excluding the current branch.
  • The preview is the commits that would be pulled in (git log --oneline HEAD..<candidate>, up to 50).
  • A confirmation prompt ([y/N]) is shown first, listing how many commits will be merged, the exact command that will run, and the conflict prediction.
  • The conflict prediction is a dry run of git merge-tree --write-tree (Git 2.38 or newer required). When it cannot be produced (older git, for example) the prompt says “Conflict prediction: skipped” and the merge still runs. “Could not predict” is never treated as “will not conflict”.
  • On a conflict, git’s message is shown as-is and the exit code is non-zero. Resolve it with the recovery menu (run gz merge again) or with plain git.
gz merge             # 通常の merge(fast-forward できる場合は fast-forward)
gz merge --no-ff     # fast-forward できる場合でもマージコミットを作る
gz merge --squash    # 結果を作業ツリー・index へ反映するだけでコミットしない
gz merge --ff-only   # fast-forward できる場合のみ merge する
オプション説明
--no-fffast-forward できる場合でもマージコミットを作成する
--squashマージ結果を作業ツリー・index へ反映するだけでコミットしない
--ff-onlyfast-forward できる場合のみ merge する
  • 3 つのオプションは相互排他です(同時に指定すると選択を始める前に拒否されます)。
  • merge 先は現在の位置(HEAD)に固定で、候補は現在のブランチを除くローカルブランチとリモート追跡ブランチです。
  • プレビューは取り込まれるコミット(git log --oneline HEAD..<候補>、最大 50 件)。
  • 実行前に確認プロンプト([y/N])を表示します。 取り込まれるコミット数・実際に実行するコマンド・コンフリクト予測を提示します。
  • コンフリクト予測は git merge-tree --write-tree のドライラン (Git 2.38 以降が必要)です。予測できない場合(2.38 未満など)は 「省略しました」と表示し、merge の実行は継続します。「予測できなかった」を 「コンフリクトしない」として扱うことはありません。
  • コンフリクトした場合は git のメッセージがそのまま表示され、非ゼロ終了します。 解決は復帰メニューgz merge を再実行)または 素の git で行います。

gz rebase — picking the rebase base gz rebase — rebase の base の選択 #

gz rebase   # pick the base and run git rebase <base>
  • Candidates are local and remote-tracking branches excluding the current branch (tags and arbitrary commits are not offered).
  • The preview is the commits that would be replayed (git log --oneline <candidate>..HEAD, up to 50).
  • Because this rewrites history, a confirmation prompt ([y/N]) is shown first, stating how many commits will be replayed and that their hashes will change.
  • On a conflict, git’s message is shown as-is and the exit code is non-zero.
gz rebase   # base を選んで git rebase <base>
  • 候補は現在のブランチを除くローカルブランチとリモート追跡ブランチです(tag や任意コミットは候補に含めません)。
  • プレビューは replay されるコミット(git log --oneline <候補>..HEAD、最大 50 件)。
  • 履歴改変操作のため、実行前に確認プロンプト([y/N])を表示します。 replay されるコミット数と、コミットハッシュが変わることを提示します。
  • コンフリクトした場合は git のメッセージがそのまま表示され、非ゼロ終了します。

The recovery menu for conflicts (gz merge / gz rebase / gz sync / gz pull) コンフリクト時の復帰メニュー(gz merge / gz rebase / gz sync / gz pull #

Running gz merge / gz rebase / gz sync / gz pull while a merge or rebase is in progress shows a recovery menu instead of the usual flow. Its contents are decided by the operation that is in progress (running gz merge during a rebase shows the rebase menu).

In progressMenu entries
merge Inspect the conflicted files and mark them resolved / resume the merge (git merge --continue) / abort the merge (git merge --abort)
rebase Inspect the conflicted files and mark them resolved / resume the rebase (git rebase --continue) / skip the current commit (git rebase --skip) / abort the rebase (git rebase --abort)
  • Whichever entry is highlighted, the preview shows the current state (git status --short --branch), so you can tell unresolved (UU) from resolved () on the spot.
  • Choosing “inspect the conflicted files and mark them resolved” lists the unmerged files. The preview shows the diff including conflict markers (<<<<<<< and friends), and the files you pick with Tab are git added (staged as resolved). Editing the files themselves is up to your own editor — fuzgit never launches one.
  • If you choose that entry with no unresolved files left, it tells you that you can resume with continue and exits.
  • Aborting loses the resolutions made so far, so a confirmation prompt ([y/N]) is shown first.
  • continue / skip run with inherited stdio, so git opens the editor when a commit message is needed.
  • There is no recovery menu for cherry-pick or revert (that is left to git’s own --continue / --abort guidance).

merge / rebase が進行中の状態で gz merge / gz rebase / gz sync / gz pull を実行すると、通常のフローではなく 復帰メニューが表示されます。メニューの内容は 進行中の操作の種類で決まります(rebase 進行中に gz merge を実行しても rebase のメニューが出ます)。

進行中メニュー項目
merge コンフリクトファイルを確認して解決済みにする / merge を再開する(git merge --continue)/ merge を中止する(git merge --abort
rebase コンフリクトファイルを確認して解決済みにする / rebase を再開する(git rebase --continue)/ 現在のコミットを飛ばす(git rebase --skip)/ rebase を中止する(git rebase --abort
  • どの項目を選んでいても、プレビューには現在の状態 (git status --short --branch)が出ます。未解決(UU 等)と 解決済み()の区別をその場で確認できます。
  • 「コンフリクトファイルを確認して解決済みにする」を選ぶと、コンフリクト中(unmerged)の ファイル一覧が出ます。プレビューにはコンフリクトマーカー (<<<<<<< 等)を含む差分が表示され、 Tab で選んだファイルを git add(=解決済みとして stage)します。 ファイルの編集そのものは各自のエディタで行ってください(fuzgit はエディタを起動しません)。
  • 未解決のファイルが 1 件も無い状態でこの項目を選んだ場合は、continue で再開できる旨を表示して終了します。
  • 中止(abort)はここまでの解決内容が失われるため、実行前に確認プロンプト([y/N])を表示します。
  • continue / skip は継承 stdio で実行するため、コミットメッセージの入力が必要な場合は git がエディタを起動します。
  • cherry-pick / revert には復帰メニューを用意していません(git の --continue / --abort の案内に委ねます)。

gz revert — picking the commits to undo gz revert — 打ち消すコミットの選択 #

gz revert             # revert the picked commits (message reviewed in the editor)
gz revert --no-edit   # commit with git's default message, without opening the editor
OptionDescription
--no-editCommit with git’s default message without opening the editor
  • Candidates are commits reachable from HEAD (1000 by default, shared with gz log --limit). The preview is git show --color=always.
  • Tab selects several commits. Whatever order you picked them in, they are reverted newest first (undoing an old commit first tends to clash with the later changes stacked on top of it — exactly the opposite of gz cherry-pick).
  • Picking several commits opens the editor once per commit unless you pass --no-edit.
  • If the selection contains a merge commit (two or more parents), fuzgit stops before running git revert. Reverting a merge commit requires -m <parent-number>, and picking the mainline is not something a fuzzy finder adds value to, so it is unsupported. On stopping, a ready-to-run git revert -m 1 <full hash> is offered (merge commits are never silently removed from the candidates).
  • On a conflict, git’s message is shown as-is and the exit code is non-zero. Continue or abort with git revert --continue / git revert --abort.
gz revert             # 選んだコミットを revert する(メッセージはエディタで確認)
gz revert --no-edit   # エディタを起動せず git の既定メッセージのままコミットする
オプション説明
--no-editエディタを起動せず、git の既定メッセージのままコミットする
  • 候補は HEAD からのコミット(既定 1000 件、gz log --limit の既定値と共通)。プレビューは git show --color=always
  • Tab複数選択できます。選んだ順に関わらず、常に新しい順に revert します(古いコミットを先に打ち消すと、その上に積まれた後続の変更と衝突しやすいため。 gz cherry-pick の古い順とはちょうど逆になります)。
  • 複数件を選ぶと --no-edit を付けない限りエディタが件数分だけ開きます。
  • 選択にマージコミット(親が 2 つ以上)が含まれる場合は、git revert を 実行する前に停止します。マージコミットの revert には -m <parent-number> の指定が必要ですが、mainline の選択は「選ぶ」価値が 乗らないため非対応です。停止時には git revert -m 1 <フルハッシュ> の形で そのまま実行できるコマンドを提示します(暗黙に候補から除外することはしません)。
  • コンフリクトした場合は git のメッセージをそのまま表示して非ゼロ終了します。 続行・中止は git revert --continue / git revert --abort で行ってください。

gz status — the state dashboard (two-step selection) gz status — 状態ダッシュボード(2 段選択) #

gz status

There are no options. It is a two-step flow: pick the files, then pick the action.

1. File selection (multi-select)

  • Candidates are staged, unstaged and untracked changes in one list (the same candidate generation as gz commit), each line prefixed with the same status code as git status. The two status characters are left-aligned: a change that only exists in the working tree reads where git writes  M, and the column stays two characters wide so the paths still line up. They are coloured the way git colours them: green for the index (staged) column, red for the working-tree column, and red for both characters of an untracked file (??). Because of the left alignment, the colour is the only thing that tells a staged change from an unstaged one — where colour is unavailable (NO_COLOR, a pipe) the two look alike.
  • The header summarises the current state in one line:
    main  |  ahead 2 / behind 1  |  staged 2 / unstaged 2 / untracked 1 / stash 1
    Without an upstream the whole ahead/behind block is omitted (on a detached HEAD, detached HEAD is shown instead of the branch name). ahead/behind is computed from the local remote-tracking ref, so it uses no network.
  • The preview is split into per-state sections: staged (git diff --cached) when there are staged changes and unstaged (git diff) when there are unstaged ones; sections that do not apply do not run git at all. Untracked files show their content as-is.

2. Action menu (single select)

EntryWhat it runs
Stage the selected files (git add)Same as gz add
Discard the changes of the selected files (git restore)Same as gz restore (with a confirmation prompt)
Stash the selected files (git stash push)Same as gz stash push (--include-untracked is added when untracked files were picked)
Commit the selected files (git commit)Same as gz commit (untracked files are git added first; message via the editor)
Print the paths of the selected files to stdoutFor pipes. Nothing but the paths is printed
  • Each action is literally the same implementation as the corresponding command, so its safeguards (restore’s confirmation prompt, commit’s pre-add of untracked files) keep working.
  • The menu preview shows the current state (git status --short --branch).
  • Every message except “print the paths to stdout” goes to stderr, so stdout stays free for pipes.
  • With no changes at all it is not an error: it exits with code 0 (checking that the tree is clean is a legitimate use of status). In that case the header-equivalent information goes to stderr.
    $ gz status
    There are no changes (the working tree is clean)
    main  |  staged 0 / unstaged 0 / untracked 0 / stash 0

オプションはありません。ファイルを選ぶ → アクションを選ぶの 2 段構成です。

1. ファイル選択(複数選択)

  • 候補はステージ済み・未ステージ・未追跡の変更を 1 つの一覧にしたもの (gz commit と同じ候補生成)で、行頭には git status と 同じ状態コードが付きます。状態コードの 2 文字は左詰めで表示します(作業ツリーだけの変更は git の  M ではなく 。列幅は 2 文字のままなのでパスの開始位置は揃います)。配色は git と同じで、index(staged)列は緑、作業ツリー列は赤、未追跡(??)は 2 文字とも赤です。左詰めにしたため、staged と unstaged の区別は色だけが担いますNO_COLOR やパイプ経由など色の付かない環境では両者が同じ見た目になります)。
  • ヘッダーに現在の状態を 1 行で表示します。
    main  |  ahead 2 / behind 1  |  staged 2 / unstaged 2 / untracked 1 / stash 1
    upstream が設定されていない場合、ahead / behind の区画ごと省略します (detached HEAD ではブランチ名の代わりに detached HEAD と表示します)。 ahead / behind はローカルのリモート追跡参照から算出するため ネットワークを使いません
  • プレビューは状態別のセクションに分かれます。ステージ済みの変更があれば stagedgit diff --cached)、未ステージの変更があれば unstagedgit diff)を表示し、該当しないセクションは git を実行しません。未追跡ファイルは内容をそのまま表示します。

2. アクションメニュー(単一選択)

項目実行内容
選択したファイルをステージする (git add)gz add と同じ
選択したファイルの変更を破棄する (git restore)gz restore と同じ(確認プロンプトあり
選択したファイルを stash へ退避する (git stash push)gz stash push と同じ(未追跡を選んだ場合は --include-untracked が付く)
選択したファイルをコミットする (git commit)gz commit と同じ(未追跡ファイルは事前に git add、メッセージはエディタ)
選択したファイルのパスを標準出力へ出力するパイプ用途。パス以外は出力しない
  • 各アクションの実体は対応する既存コマンドと同じ実装であり、安全策 (restore の確認プロンプト、commit の未追跡ファイルの事前 add など)も そのまま働きます。
  • メニューのプレビューには現在の状態(git status --short --branch)を表示します。
  • 「パスを標準出力へ出力する」以外のメッセージ類はすべて標準エラーへ出すため、標準出力はパイプ用途に空いています。
  • 変更が 1 件も無い場合はエラーにせず、終了コード 0 で終わります (クリーンな状態の確認も status の用途のため)。この場合はヘッダー相当の情報を 標準エラーへ出します。
    $ gz status
    変更はありません(作業ツリーはクリーンです)
    main  |  staged 0 / unstaged 0 / untracked 0 / stash 0

gz diff — pick what to compare and show the diff gz diff — 比較対象を選んで差分表示 #

gz diff              # unstaged changes (same as git diff)
gz diff --staged     # staged changes (same as git diff --staged)
gz diff --head       # HEAD against the working tree (including staged changes)
gz diff --upstream   # HEAD against the upstream (the remote-tracking ref)
gz diff --branch     # pick two branches and compare them
gz diff --commit     # pick two commits and compare them
OptionDescription
--stagedTarget the staged changes (same as git diff --staged)
--headCompare HEAD with the working tree (including staged changes)
--upstreamCompare HEAD with the upstream
--branchPick two branches and compare them
--commitPick two commits and compare them
  • The comparison modes are mutually exclusive. The flag names follow git’s own vocabulary.
  • --branch / --commit open the fuzzy finder twice, for the left then the right side. The header tells you which one you are picking (1/2 branch to compare from, then 2/2 branch to compare to). Candidates and previews are shared with gz branch and gz log.
  • Once the range is fixed, the list of changed files appears so you can narrow it down with Tab. The header shows the chosen range, and the preview is the coloured diff limited to the highlighted file.
  • On confirmation it runs git diff <range> -- <pathspec>... with inherited stdio (pager and colour are left to git). Revision comparisons are passed as two arguments, git diff <a> <b>, not as <a>..<b>.
  • --upstream stops with a dedicated error when there is no upstream or on a detached HEAD. The right-hand side is the local remote-tracking ref (refs/remotes/<remote>/<branch>), so it uses no network. Run gz fetch first if you want to compare against the latest state.
  • When the range contains no changed files it is not an error: it exits with code 0.
    $ gz diff
    There is no difference (Unstaged changes: index and work tree)
gz diff              # 未ステージの変更(git diff と同じ)
gz diff --staged     # ステージ済みの変更(git diff --staged と同じ)
gz diff --head       # HEAD と作業ツリー(ステージ済みの変更も含む)
gz diff --upstream   # HEAD と upstream(リモート追跡参照)
gz diff --branch     # ブランチを 2 回選んで比較する
gz diff --commit     # コミットを 2 回選んで比較する
オプション説明
--stagedステージ済みの変更を対象にする(git diff --staged と同じ)
--headHEAD と作業ツリーを比較する(ステージ済みの変更を含む)
--upstreamHEAD と upstream を比較する
--branchブランチを 2 回選択して比較する
--commitコミットを 2 回選択して比較する
  • 比較モードは相互排他です。フラグ名は git 本体の語彙に合わせています。
  • --branch / --commit は比較元・比較先の順に fuzzy finder を 2 回起動します。どちらを選んでいるかはヘッダー (1/2 比較元のブランチ / 2/2 比較先のブランチ)で分かります。 候補・プレビューはそれぞれ gz branch / gz log と共通です。
  • 比較範囲が確定すると変更ファイル一覧が出るので、Tab で表示したいファイルを 絞り込めます。ヘッダーには確定した比較範囲を表示し、プレビューは選択中ファイルに 限定した色付き差分です。
  • 決定すると git diff <範囲> -- <pathspec>... を継承 stdio で 実行します(ページャ・色は git に委ねます)。リビジョン同士の比較は <a>..<b> ではなく git diff <a> <b> の 2 引数で渡します。
  • --upstream は upstream が設定されていない場合・detached HEAD の場合に 専用エラーで停止します。比較先はローカルのリモート追跡参照 (refs/remotes/<remote>/<branch>)であり、 ネットワークを使いません。最新の状態と比べたい場合は先に gz fetch を実行してください。
  • 比較範囲に変更ファイルが 1 件も無い場合はエラーにせず、終了コード 0 で終わります。
    $ gz diff
    差分はありません(未ステージの変更: index と作業ツリー)

gz fetch — deciding what to fetch gz fetch — fetch 対象の取得 #

gz fetch               # decide what (which remote) to fetch and run git fetch
gz fetch --prune       # also clean up tracking refs for branches deleted on the remote
gz fetch --siblings    # pick repositories sitting next to this one and fetch them all
OptionDescription
-p, --pruneDelete tracking refs for branches deleted on the remote (git fetch --prune)
-s, --siblingsAlso target repositories sitting at the same level as the current one (multi-select)

The point of this command is not “pick a remote” but “decide what to fetch”. The fuzzy finder only opens when there is actually a choice.

Default (the current repository)

  • The finder only opens when there is more than one remote. Candidates are the registered remote names plus a fixed entry, all remotes, which runs git fetch --all.
  • With exactly one remote the finder does not open and the fetch runs immediately, because choosing between the only remote and “all remotes” would contact the same place either way. So that you can still tell what was contacted, one line naming the target is printed to stderr before running (git fetch can print nothing at all when there are no updates).
    $ gz fetch
    Fetching from the remote `origin` (only one remote is registered, so the selection was skipped)
  • The preview only shows local information (two sections: the remote URL and the known remote-tracking branches). A remote that has never been fetched has no tracking refs, so that section is omitted entirely. Not using the network in previews is a design rule — see Network operations.
  • With no remotes registered at all it suggests git remote add and exits.

--siblings — fetching the repositories next door

Every other fuzgit command touches only the current repository. --siblings is the single exception: only when the flag is given, it scans the parent directory of the current worktree root, one level deep only (no recursion) and offers the directories that contain a .git.

$ 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
  • A candidate line is <directory name> <remote>/<current branch>, with one such pair per remote. Repositories whose HEAD does not point at a branch have no branch to pair and show <directory name> <remotes> detached HEAD. Directory names vary in length, so each column is padded to the widest value in the list and the column to its right starts at the same place on every line. Information that would need one git process per repository (ahead/behind, for example) is kept out of the list and moved to the preview, to keep the first paint fast.
  • The current repository starts selected (Tab deselects it). The others start unselected.
  • The preview is local information only (the URL of each remote, and the tracking state of each local branch via git for-each-ref refs/heads). It runs with --no-optional-locks so it never takes a lock in somebody else’s repository.
    ── origin ──
    git@github.com:example/big-repo.git
    ── Branch tracking status ──
      develop → origin/develop [ahead 2]
    * main → origin/main [behind 4]
    The working tree state (git status) is deliberately not shown: git status walks the whole working tree (refreshing the index) before printing anything, which can take seconds per preview in a repository with many files and would stall every cursor move. The ahead/behind you need in order to decide about a fetch is in the tracking state above.
  • Repositories with no remote registered, and bare repositories, cannot be fetched, so they are excluded — and the number excluded is shown in the header (they are never dropped silently).
  • Linked worktrees of the same repository sitting next door are deduplicated into one entry (so the same repository is not contacted twice). Symlinked directories are not followed.
  • When the only target is the current repository the finder does not open; the reason is printed in one line and the fetch runs.
    $ gz fetch --siblings
    The current repository is the only target, so the selection was skipped
    [1/1] only
    1 succeeded / 0 failed
  • What runs in each repository is git fetch [--prune] --all. They run one at a time, serially, in candidate order (never in parallel). Before each one, [<n>/<total>] <directory name> is printed to stderr; without that line you could not tell which repository the update table belongs to, since git writes it to stderr as well.
  • A failing repository does not stop the run; the rest still execute and a summary of successes, failures and the names of the failed repositories is printed at the end. If even one fails, the exit code is 1 (the reason was already shown by git, so fuzgit does not repeat it).
    $ gz fetch --siblings
    [1/3] mike
    [2/3] alpha
    [3/3] zulu
    fatal: '/path/to/does-not-exist.git' does not appear to be a git repository
    fatal: Could not read from remote repository.
    2 succeeded / 1 failed (failed: zulu)
    error: Fetching failed for some repositories
  • If launching git itself fails (git not installed, for example) the run stops there, because that is an environment problem rather than a per-repository failure.
  • Combined with --prune, the flag applies to every selected repository. The finder header says so: --prune: applies to every selected repository.
  • If there is no repository that can be fetched at all, it stops with a dedicated error. The same happens when the current repository is bare, or when the parent directory of the worktree root cannot be determined (a repository directly at the filesystem root, for instance) — the scan range itself cannot be decided. In none of these cases does it silently fall back to just the current repository, so the range you asked for and the place it contacts can never diverge.
    $ gz fetch --siblings
    error: There is no repository to fetch (a repository without a remote and a bare repository are not offered)

Common to both

  • git fetch runs with inherited stdio, so the list of updated refs, authentication prompts and progress output are git’s own.
  • Network and authentication failures are shown with git’s message as-is and exit non-zero (fuzgit adds no timeout and no retry).
gz fetch               # fetch 対象(リモート)を決めて git fetch する
gz fetch --prune       # リモートで削除されたブランチの追跡参照も掃除する
gz fetch --siblings    # 隣に並ぶリポジトリを選んでまとめて fetch する
オプション説明
-p, --pruneリモートで削除されたブランチの追跡参照を削除する(git fetch --prune
-s, --siblings現在のリポジトリと同じ階層に並ぶリポジトリも対象に含める(複数選択)

このコマンドの主機能は「リモートを選ぶ」ことではなく 「fetch 対象の決定」です。 選択の余地がある場合にだけ fuzzy finder を起動します。

既定(現在のリポジトリ)

  • リモートが複数ある場合だけ finder を起動します。候補は登録済みの リモート名に、固定候補「すべてのリモート」を加えたものです。「すべてのリモート」を 選ぶと git fetch --all を実行します。
  • リモートが 1 つだけの場合は finder を起動せず、そのまま fetch します。 唯一のリモートと「すべてのリモート」の 2 択は、どちらを選んでも通信先が同じためです。 何に対して通信したのかが分かるよう、実行前に対象を 1 行だけ標準エラーへ示します (git fetch は更新が無ければ何も出力しないことがあるため)。
    $ gz fetch
    リモート `origin` から取得します(登録されているリモートが 1 つのため、選択を省略しました)
  • プレビューはローカル情報だけを表示します (「リモート URL」と「既知のリモート追跡ブランチ」の 2 セクション)。一度も fetch して いないリモートは追跡参照が無いため、そのセクションごと省略されます。 プレビューでネットワークを使わないのは設計上の原則です (ネットワーク操作について参照)。
  • リモートが 1 つも登録されていない場合は git remote add を促して終了します。

--siblings — 隣のリポジトリもまとめて取得

fuzgit の他のコマンドは現在のリポジトリだけを操作します。 --siblings はその唯一の例外で、フラグを明示したときに限り、 現在のリポジトリのワークツリー root の親ディレクトリ直下のみを走査して (再帰しません)、.git を持つディレクトリを候補にします。

$ gz fetch --siblings
現在のリポジトリを選択済みにしています。Tab: 選択の切替 / Enter: 取得  |  除外 1 件(リモート未登録 / bare)
>> mike   origin/main
   alpha  origin/main
   zulu   origin/main
  • 候補行は ディレクトリ名 リモート/現在のブランチ です(リモートが複数あれば その数だけ並べます)。HEAD がブランチを指していないリポジトリは対にするブランチが無いため、 ディレクトリ名 リモート detached HEAD と表示します。 ディレクトリ名の長さはまちまちであるため、各列は候補一覧の中で最も長いものに合わせて 空白で埋め、右の列の開始位置をすべての行で揃えます。ahead / behind のように 1 リポジトリあたり git の起動が必要な情報は候補一覧に載せず、プレビューへ回しています (初期表示を速く保つため)。
  • 現在のリポジトリは起動時から選択済みです(Tab で外せます)。他のリポジトリは未選択です。
  • プレビューはローカル情報だけです(リモートごとの URL と、 git for-each-ref refs/heads によるローカルブランチごとの追跡状況)。 他人のリポジトリでロックを取らないよう --no-optional-locks を付けて 実行します。
    ── origin ──
    git@github.com:example/big-repo.git
    ── ブランチの追跡状況 ──
      develop → origin/develop [ahead 2]
    * main → origin/main [behind 4]
    作業ツリーの状態(git status)は表示しません。git status は 状態を出す前に作業ツリー全体を走査する(インデックスを refresh する)ため、 ファイル数の多いリポジトリではプレビュー 1 件の生成に数秒掛かることがあり、 カーソルを動かすたびに待たされるためです。fetch の判断に必要な ahead / behind は 上記の追跡状況で分かります。
  • リモートが 1 つも登録されていないリポジトリと bare リポジトリは fetch できないため 候補から除外し、除外した件数をヘッダーに示します(黙って消しません)。
  • 同じリポジトリの linked worktree が隣に並んでいる場合は1 件に重複排除します (同じリポジトリへ二重に通信しないため)。シンボリックリンクのディレクトリは辿りません。
  • 対象が現在のリポジトリ 1 件だけの場合は finder を起動せず、 理由を 1 行示してそのまま実行します。
    $ gz fetch --siblings
    対象が現在のリポジトリ 1 件のため、選択を省略しました
    [1/1] only
    成功 1 件 / 失敗 0 件
  • 実行するのは各リポジトリでの git fetch [--prune] --all です。 候補一覧の順序で 1 件ずつ直列に実行します(並列にしません)。 実行前に [<何番目>/<全体>] <ディレクトリ名> を標準エラーへ 表示します。git fetch の更新表も標準エラーに出るため、この 1 行が無いと どのリポジトリの出力なのか読み取れなくなります。
  • 途中のリポジトリが失敗しても中断せず残りを実行し、最後に成功件数・ 失敗件数と失敗したリポジトリ名を集計表示します。1 件でも失敗した場合は 終了コード 1 で終わります(失敗理由は git のメッセージがその場で 表示済みのため、fuzgit は再掲しません)。
    $ gz fetch --siblings
    [1/3] mike
    [2/3] alpha
    [3/3] zulu
    fatal: '/path/to/does-not-exist.git' does not appear to be a git repository
    fatal: Could not read from remote repository.
    成功 2 件 / 失敗 1 件(失敗: zulu)
    エラー: 一部のリポジトリで取得に失敗しました
  • git の起動そのものに失敗した場合(git 未インストール等)は、 その場で中断します。環境の問題でありリポジトリごとの失敗ではないためです。
  • --prune と併用した場合は選択したすべてのリポジトリに適用されます。 その旨は finder のヘッダーに --prune: 選択したすべてのリポジトリに適用 として表示します。
  • fetch できるリポジトリが 1 件も無い場合は専用エラーで停止します。また、現在のリポジトリが bare の場合とワークツリー root の親ディレクトリを取得できない場合(リポジトリが ファイルシステムの root 直下にある等)は、走査範囲そのものを決められないため専用エラーで 停止します。いずれも暗黙に現在のリポジトリだけへ倒すことはしません (指定した範囲と通信先が食い違わないようにするため)。
    $ gz fetch --siblings
    エラー: fetch できるリポジトリがありません(リモートが登録されていないリポジトリと bare リポジトリは対象になりません)

共通

  • git fetch は継承 stdio で実行するため、更新された参照の一覧・認証プロンプト・進捗表示は git のものがそのまま出ます。
  • ネットワーク・認証の失敗は git のメッセージをそのまま表示して非ゼロ終了します(fuzgit 側でのタイムアウト・リトライは行いません)。

gz pull — bringing several branches up to date gz pull — 複数ブランチの一括取り込み #

gz pull    # pick the branches and fast-forward them onto their upstream

There are no options: no integration flags (--rebase / --merge), no --prune, and no positional branch name.

The only thing you pick is which branches should follow their upstream. The targets are several local branches of the current repository; you never pick a remote × branch combination, because the destination is fixed to each branch’s own upstream.

  • Integration is fixed to fast-forward only. “Roll back to the state before the pull if a conflict happens” is satisfied not by a rollback routine but by never performing an operation that can break anything. A fast-forward does not recreate existing commits, and git refuses without changing anything when it cannot fast-forward, when the change would clash with the working tree, or when the branch is in use by another worktree. A failure therefore leaves the pre-pull state intact and there is nothing to roll back. (git merge --abort cannot always reconstruct the original state when there are uncommitted changes, and git reset --hard discards uncommitted changes, so neither is used.)
  • Candidates are local branches that have an upstream whose branch.<name>.remote is a registered remote name. Branches without an upstream, with an unregistered remote, or checked out in another worktree have no determinable destination, so they are excluded — and the number excluded is shown in the header (never dropped silently).
  • The current branch is put first, marked with a leading (as in gz branch), and starts selected (Tab deselects it). On a detached HEAD there is simply no current branch, and the other branches are offered as usual (unlike gz sync, this is not an error).
    $ 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
  • A candidate line is <mark><branch> → <short upstream name>. ahead / behind is not shown. At candidate generation time all that is known is the difference against the tracking refs from the last fetch, which does not match how much is about to be fetched and integrated (showing it would give the false impression of “not behind”; the actual amount appears in git’s own update table at run time).
  • The preview is headed ── Commits not integrated yet (as of the last fetch) ── and shows the commits between the branch and its locally stored tracking ref (git log --oneline <branch>..<tracking ref>). The heading states that the information is as of the last fetch: the preview uses no network (see Network operations).
  • With only one candidate the finder does not open; the target and the reason are printed in one line and the run starts.
    $ gz pull
    Integrating `main` (there is only one candidate, so the selection was skipped)
    [1/1] main
    1 succeeded / 0 failed
  • With no candidates at all it stops and explains how to set an upstream (git push -u <remote> <branch> / git branch --set-upstream-to=<remote>/<branch>).
  • Execution starts with git fetch <remote> once per remote that the selected upstreams belong to (never once per branch). If one remote fails to fetch, every branch whose upstream lives on that remote is recorded as failed and skipped, while branches on other remotes continue — so nothing is ever integrated implicitly from a stale tracking ref.
  • Integration then happens one branch at a time, serially, in candidate order (never in parallel). Before each one, [<n>/<total>] <branch> is printed to stderr (same format as gz fetch --siblings), because git’s update tables and rejection reasons also go to stderr.
  • The currently checked out branch is integrated with git merge --ff-only <tracking ref> (the working tree and index have to be updated), while every other branch uses git fetch . <tracking ref>:refs/heads/<branch> (updating only the ref, never touching the working tree). The latter refspec deliberately omits the forcing +: with it, non-fast-forward updates would go through and commits that exist only on that branch would be lost.
  • A failing branch does not stop the run; the rest still execute and successes, failures and the failed branch names are summarised at the end. If even one fails the exit code is 1 (git already printed the reason, so fuzgit does not repeat it). Only when at least one branch could not fast-forward is a one-line pointer to the next step added.
    $ 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
    error: Integrating failed for some branches
    It only points at it — gz sync is never started automatically. How history is integrated (rebase or merge) stays an explicit user choice.
  • If launching git itself fails (git not installed, for example) the run stops there, because that is an environment problem rather than a per-branch failure.
  • There is no [y/N] confirmation prompt. Making a multi-selection and pressing Enter is already an explicit decision, and a fast-forward loses neither existing commits nor working tree changes (the same judgement as gz fetch --siblings).
  • Run while a merge or rebase is in progress, it shows the recovery menu instead of the branch selection (same as gz sync).

gz pull vs gz sync

Aspectgz pullgz sync
TargetSeveral local branches that you pickThe current branch only (fixed to its upstream)
IntegrationFast-forward only (fixed)Selectable with --rebase / --merge (default: fast-forward only)
Selection UIMulti-select of branchesNone
Confirmation promptNoYes

Use gz pull to bring several branches along at once, and gz sync to integrate a single branch with a method of your choice. A branch that gz pull could not fast-forward is integrated by switching to it and running gz sync --rebase / gz sync --merge.

gz pull    # 取り込むブランチを選んで、upstream へ fast-forward で追随させる

オプションはありません。取り込み方式のフラグ(--rebase / --merge)も --prune もブランチ名の位置引数も受け付けません。

選ばせるのは「どのブランチを upstream へ追随させるか」だけです。 対象は現在のリポジトリの複数のローカルブランチで、リモート × ブランチの組み合わせは 選ばせません(追随先は各ブランチの upstream に固定です)。

  • 取り込みは fast-forward のみに固定です。「コンフリクトが起きたら pull 前の状態に戻す」ことをロールバック処理ではなく、壊し得る操作を 行わないことで満たすためです。fast-forward は既存のコミットを作り直さず、 fast-forward できない場合・作業ツリーの変更と衝突する場合・他の worktree で 使用中の場合はいずれも git が何も変更せずに拒否します。そのため 失敗しても取り込み前の状態がそのまま残り、巻き戻す対象がありません (git merge --abort は未コミットの変更があると元の状態を再構成できない ことがあり、git reset --hard は未コミットの変更を破棄するため、 いずれも使いません)。
  • 候補は upstream が設定され、その branch.<name>.remote が 登録済みのリモート名であるローカルブランチです。upstream が無いもの・ リモートが登録されていないもの・他の worktree でチェックアウト中のものは 取り込み先を決められないため候補から除外し、除外した件数をヘッダーに 示します(黙って消しません)。
  • 現在のブランチは一覧の先頭に置き、行頭を gz branch と同じ)でマークして、起動時から選択済みにします (Tab で外せます)。detached HEAD の場合は現在のブランチが無いだけで、 他のブランチは通常どおり候補になります(gz sync と 違いエラーにはしません)。
    $ gz pull
    現在のブランチを選択済みにしています。Tab: 選択の切替 / Enter: fast-forward のみで取り込み  |  除外 2 件(upstream 未設定 / リモート未登録 / 他の worktree で使用中)
    >> * main       →  origin/main
         feature-a  →  origin/feature-a
  • 候補行は <マーク><ブランチ名> → <upstream の短縮名> です。 ahead / behind は表示しません。候補生成の時点で分かるのは前回の fetch までに取得済みの追跡参照との差であり、これから fetch して取り込む本数とは 一致しないためです(「遅れていない」という誤った印象を与えないため。取り込み量は 実行時に git 自身が出す更新表で分かります)。
  • プレビューは ── 未取り込みのコミット(前回の fetch 時点) ── の見出しを 付けて、ローカルに保存済みの追跡参照との差分コミット (git log --oneline <ブランチ>..<追跡参照>)を表示します。 それが前回の fetch 時点の情報であることを見出しで示しており、 プレビューではネットワークを使いません(ネットワーク操作について参照)。
  • 候補が 1 件だけの場合は finder を起動せず、対象と省略の理由を 1 行示してそのまま実行します。
    $ gz pull
    `main` を取り込みます(候補が 1 件のため、選択を省略しました)
    [1/1] main
    成功 1 件 / 失敗 0 件
  • 候補が 1 件も無い場合は、upstream の設定方法(git push -u <remote> <branch> / git branch --set-upstream-to=<remote>/<branch>)を案内して停止します。
  • 実行はまず、選択したブランチの upstream が属するリモートごとに 1 回だけ git fetch <remote> します(ブランチごとに通信しません)。 あるリモートの取得に失敗した場合は、そのリモートを upstream に持つブランチをすべて 失敗として記録して飛ばし、他のリモートのブランチは続行します(古い追跡参照のまま 暗黙に取り込まないためです)。
  • 取り込みは候補一覧の順序で 1 ブランチずつ直列に実行します (並列にしません)。実行前に [<何番目>/<全体>] <ブランチ名> を 標準エラーへ表示します(gz fetch --siblings と同形式)。git 自身の更新表や 拒否の理由も標準エラーに出るため、この 1 行が無いとどのブランチの出力なのか 読み取れなくなります。
  • 現在チェックアウト中のブランチは git merge --ff-only <追跡参照>(作業ツリーと index を 更新する必要があるため)、それ以外のブランチは git fetch . <追跡参照>:refs/heads/<ブランチ> (参照だけを更新し作業ツリーに触れない)で取り込みます。後者の refspec に強制更新の +付けません。付けると非 fast-forward の更新が通り、 そのブランチにしか無いコミットを失わせるためです。
  • 途中のブランチが失敗しても中断せず残りを実行し、最後に成功件数・ 失敗件数と失敗したブランチ名を集計表示します。1 件でも失敗した場合は 終了コード 1 で終わります(失敗理由は git のメッセージがその場で 表示済みのため、fuzgit は再掲しません)。fast-forward できなかったブランチが 1 件以上あるときだけ、次の手段を 1 行案内します。
    $ gz pull
    [1/4] main
    [2/4] alpha
    [3/4] diverged
    [4/4] zeta
    成功 3 件 / 失敗 1 件(失敗: diverged)
    fast-forward できなかったブランチは、そのブランチへ切り替えてから `gz sync --rebase` または `gz sync --merge` で取り込めます
    エラー: 一部のブランチで取り込みに失敗しました
    案内するだけで、gz sync を自動起動することはありません。 履歴の統合方法(rebase か merge か)はユーザーの明示指定に限ります。
  • git の起動そのものに失敗した場合(git 未インストール等)は、 その場で中断します。環境の問題でありブランチごとの失敗ではないためです。
  • 実行前の [y/N] 確認プロンプトはありません。 複数選択して Enter を押すこと自体が明示的な決定であり、fast-forward は 既存のコミットも作業ツリーの変更も失わせないためです (gz fetch --siblings と同じ判断)。
  • merge / rebase が進行中の状態で実行した場合は、ブランチ選択ではなく 復帰メニューが表示されます(gz sync と同じ)。

gz sync との使い分け

観点gz pullgz sync
対象選んだ複数のローカルブランチ現在のブランチ 1 本(upstream 固定)
取り込み方式fast-forward のみ(固定)--rebase / --merge で選べる(既定は fast-forward のみ)
選択 UIブランチの複数選択なし
確認プロンプトなしあり

まとめて追随させたいときは gz pull、方式を選んで 1 本だけ 取り込みたいときは gz sync です。gz pull で fast-forward できなかったブランチは、そのブランチへ切り替えてから gz sync --rebase / gz sync --merge で取り込みます。

gz sync — syncing with the upstream gz sync — upstream との同期 #

gz sync            # fetch, then fast-forward (default)
gz sync --rebase   # fetch, then rebase onto the upstream (rewrites history)
gz sync --merge    # fetch, then merge the upstream
OptionDescription
-r, --rebaseIntegrate by rebasing onto the upstream (rewrites history)
--mergeIntegrate by merging the upstream
  • The target is fixed to the upstream of the current branch. The fuzzy finder never opens, because there is nothing to pick. (To integrate from an arbitrary remote or branch, use gz fetch followed by gz merge / gz rebase.)
  • With no upstream, or on a detached HEAD, it stops with a dedicated error before running git fetch. The same happens — before any network access — when the upstream’s branch.<name>.remote is not a registered remote name.
  • The flow is git fetch <remote> → recompute ahead/behind (now that the tracking ref is updated) → confirmation prompt → integrate.
  • When behind is 0 it reports that you are up to date and exits with code 0.
  • The default is fast-forward only (git merge --ff-only). When it cannot fast-forward (diverged), git’s fatal: Not possible to fast-forward, aborting. is shown as-is and the command stops — it never silently falls back to merge or rebase. State the method explicitly with --rebase / --merge (the two are mutually exclusive).
  • A confirmation prompt ([y/N]) is shown before integrating, listing how many commits will come in and the exact command; with --rebase it also states that history will be rewritten.
  • Run while a merge or rebase is in progress, it shows the recovery menu.
  • There is no --prune. Deleting tracking refs is an explicit operation, done with gz fetch --prune, not a side effect of syncing.
gz sync            # fetch → fast-forward(既定)
gz sync --rebase   # fetch → upstream の上へ rebase(履歴改変)
gz sync --merge    # fetch → upstream を merge
オプション説明
-r, --rebaseupstream の上へ rebase して取り込む(履歴改変)
--mergeupstream を merge して取り込む
  • 対象は現在ブランチの upstream に固定です。fuzzy finder は 起動しません(選ぶ対象が無いため。任意のリモート・ブランチから取り込みたい場合は gz fetch のあとに gz merge / gz rebase を使ってください)。
  • upstream が設定されていない場合・detached HEAD の場合は、 git fetch を実行する前に専用エラーで停止します。 upstream の branch.<name>.remote が登録済みのリモート名でない場合も 同様に、ネットワークへ出る前に停止します。
  • 処理の流れは git fetch <remote> →(追跡参照が更新された状態で) ahead / behind の再計算 → 確認プロンプト → 取り込みの実行です。
  • behind が 0 の場合は「最新です」と表示して終了コード 0 で終わります。
  • 既定は fast-forward のみ(git merge --ff-only)です。 fast-forward できない(diverged)場合は git の fatal: Not possible to fast-forward, aborting. をそのまま表示して停止し、 暗黙に merge / rebase へ倒すことはありません。取り込み方法は --rebase / --merge で明示してください(2 つは相互排他)。
  • 取り込みの実行前に確認プロンプト([y/N])を表示します。 取り込まれるコミット数と実際に実行するコマンドを提示し、--rebase の場合は 履歴改変になる旨も併記します。
  • merge / rebase が進行中の状態で実行した場合は、復帰メニューが表示されます。
  • --prune はありません。追跡参照の削除は gz fetch --prune として 明示的に行う操作であり、同期のついでには行いません。

gz worktree — listing and managing worktrees gz worktree — worktree の一覧・管理 #

gz worktree                  # pick from the list and print the path to stdout
cd "$(gz worktree)"          # shell integration (move to the picked worktree)
gz worktree add <path>       # pick a branch and create a new worktree
gz worktree remove           # pick a worktree and remove it (with confirmation)
gz worktree prune            # tidy up bookkeeping for worktrees whose directory is gone (with confirmation)

No arguments (list, then print the path)

  • A candidate line is <path> main|linked <branch|detached|bare>[ locked][ prunable].
  • The preview shows the commit log of the highlighted worktree’s HEAD (bare worktrees and worktrees without a single commit get no preview).
  • On confirmation only the path of the picked worktree goes to stdout; messages go to stderr.

Note (known behaviour): if you redirect stdout to a file, skim’s drawing escapes end up in that file too (gz worktree > out.txt and gz log > log.txt behave the same). With pipes and command substitution ($(gz worktree)) the drawing goes to the terminal, so cd "$(gz worktree)" works as expected.

gz worktree add <path>

ArgumentDescription
<PATH>Path of the worktree to create (required positional argument)
  • What you pick in the fuzzy finder is the branch to check out. Candidates are limited to local branches not in use by another worktree (git cannot check out the same branch in two worktrees at once).
  • It runs git worktree add -- <path> <branch>. Directory names are never suggested automatically.
  • For a path starting with -, write gz worktree add -- -dashy (that -- is clap’s; fuzgit adds its own -- when calling git).

gz worktree remove

  • Candidates are linked worktrees only — the main worktree is not offered (git does not allow removing it).
  • A confirmation prompt ([y/N]) is shown first.
  • Locked worktrees and worktrees with uncommitted changes fail with git’s message as-is and a non-zero exit code (--force is not offered).

gz worktree prune

  • It first runs git worktree prune --dry-run --verbose and puts the worktrees that would be pruned, with the reason, into the confirmation prompt. git worktree prune runs only if you approve.
  • With nothing to prune it reports so and exits with code 0.
gz worktree                  # 一覧から選んでパスを標準出力へ出す
cd "$(gz worktree)"          # シェル連携(選んだ worktree へ移動する)
gz worktree add <path>       # ブランチを選んで新しい worktree を作る
gz worktree remove           # worktree を選んで削除する(確認あり)
gz worktree prune            # 実体を失った worktree の管理情報を整理する(確認あり)

引数なし(一覧 → パス出力)

  • 候補行は <パス> main|linked <ブランチ|detached|bare>[ locked][ prunable] です。
  • プレビューには選択中 worktree の HEAD のコミットログを表示します(bare とコミットが 1 件も無い worktree はプレビューを出しません)。
  • 決定すると選んだ worktree のパスだけを標準出力へ出します。メッセージ類は標準エラーです。

注意(既知の挙動): 標準出力をファイルへリダイレクトすると、 skim の描画エスケープもそのファイルへ混ざります (gz worktree > out.txt / gz log > log.txt のいずれも同じ)。 パイプ・コマンド置換($(gz worktree))では描画は端末側へ出るため、 cd "$(gz worktree)" は期待どおり動作します。

gz worktree add <path>

引数説明
<PATH>作成する worktree のパス(必須の位置引数)
  • fuzzy finder で選ぶのはチェックアウトするブランチです。候補は 他の worktree で使用中でないローカルブランチに限ります (git は同じブランチを複数の worktree で同時にチェックアウトできないため)。
  • 実行するのは git worktree add -- <path> <branch> です。ディレクトリ名の自動提案は行いません。
  • - で始まるパスを指定する場合は gz worktree add -- -dashy のように 書きます(clap 側の -- であり、fuzgit は git へ渡す際にも別途 -- で保護します)。

gz worktree remove

  • 候補は linked worktree のみで、main worktree は候補に含めません(git が削除を許可しないため)。
  • 実行前に確認プロンプト([y/N])を表示します。
  • locked な worktree・未コミット変更を含む worktree は、git のエラーをそのまま表示して非ゼロ終了します(--force は提供していません)。

gz worktree prune

  • 先に git worktree prune --dry-run --verbose を実行し、整理される worktree と 理由を確認プロンプトに提示します。承認された場合のみ git worktree prune を 実行します。
  • 対象が 1 件も無い場合は「整理する worktree はありません」と表示して終了コード 0 で終わります。

Network operationsネットワーク操作について #

  • The only commands that use the network are gz fetch, gz pull and gz sync. Every other command works purely from local repository data (pushing is left to plain git push).
  • Candidate generation and previews never use the network. This is a design rule. A preview is regenerated every time the highlighted entry changes, so a network round trip there (latency, timeouts, authentication prompts) would block the display. Accordingly the gz fetch preview is limited to local data (git remote get-url / git for-each-ref refs/remotes/<remote>) and never runs git fetch --dry-run inside a preview; the list of updated refs is printed by git fetch itself when it runs. The gz pull preview (commits not yet integrated) likewise comes from reading locally stored tracking refs and is therefore as of the last fetch.
  • The only thing gz pull sends over the network is the git fetch <remote> per remote, run after the targets are fixed. Branches sharing an upstream remote are handled together, so there is one round trip per remote and never one per branch. Updating a branch that is not checked out (git fetch . <tracking ref>:refs/heads/<branch>) fetches from the repository itself and never goes out to the network.
  • The ahead/behind in gz status and the right-hand side of gz diff --upstream both come from reading local remote-tracking refs. Run gz fetch first if you want to compare against the latest state.
  • Authentication is left to git. git fetch / git push run with inherited stdio, so credential entry, credential helpers and progress output are all git’s own. fuzgit performs no timeouts, no retries and no direct socket handling whatsoever.
  • gz fetch --siblings and gz pull run the selected targets one at a time, serially (never in parallel). git itself also fetches multiple remotes sequentially by default (see man git-fetch). Running them in parallel would interleave the inherited stdio, and with several SSH passphrase or credential helper prompts running at once you could not tell which remote you were typing into. Ctrl-C during a run is not handled specially: the SIGINT delivered to the foreground process group terminates fuzgit along with it (the remaining targets are not run).
  • Without --siblings, neighbouring repositories are not even offered as candidates. To prevent a mere change of directory from firing traffic at many repositories, the scan origin is always derived from the current repository and cannot be given as a path.
  • ネットワークを使うのは gz fetchgz pullgz sync だけです。ほかのコマンドはすべてローカルのリポジトリ情報だけで 動作します(push は素の git push に委ねます)。
  • 候補一覧の生成とプレビューではネットワークを使いません。 これは設計上の原則です。プレビューは選択項目が変わるたびに生成されるため、 そこでネットワーク往復(遅延・タイムアウト・認証プロンプト)が発生すると描画が ブロックされてしまいます。したがって gz fetch のプレビューは ローカル情報(git remote get-url / git for-each-ref refs/remotes/<remote>)に限定しており、 git fetch --dry-run をプレビュー内で実行することはしません。 更新される参照の一覧は git fetch 自身が実行時に表示します。 gz pull のプレビュー(未取り込みのコミット)も同様に、ローカルに 保存済みの追跡参照を読んだ結果であり、前回の fetch 時点の情報です。
  • gz pull が通信するのは、対象を確定したあとに実行する リモートごとの git fetch <remote> だけです。 同じリモートを upstream に持つブランチをまとめて扱うため、通信は 1 リモートにつき 1 回で、ブランチごとには通信しません。チェックアウトしていないブランチの更新 (git fetch . <追跡参照>:refs/heads/<ブランチ>)は取得元が 自分自身のリポジトリであり、ネットワークへは出ません。
  • gz status の ahead / behind と gz diff --upstream の比較先は、いずれもローカルのリモート追跡参照を 読んだ結果です。最新の状態と比べたい場合は先に gz fetch を実行してください。
  • 認証は git に委ねます。git fetch / git push は継承 stdio で実行するため、認証情報の入力・credential helper・ 進捗表示はすべて git のものがそのまま動きます。fuzgit 側でのタイムアウト・リトライ・ ソケットの直接操作は一切行いません。
  • gz fetch --siblingsgz pull は選択した対象を 1 件ずつ直列に実行します(並列にしません)。git 自身も複数リモートの fetch を 既定で逐次実行します(man git-fetch)。並列にすると継承 stdio の出力が混ざるうえ、 SSH パスフレーズや credential helper のプロンプトが同時に複数走るとどのリモートへ 入力しているのか判別できなくなるためです。実行中の Ctrl-C は独自に処理せず、 前面プロセスグループへ届いた SIGINT で fuzgit ごと終了します(残りは実行されません)。
  • --siblings を指定しない限り、隣のリポジトリは候補にすら なりません。ディレクトリを移動しただけで多数のリポジトリへ通信が飛ぶことを 防ぐため、探索の起点は常に現在のリポジトリから導出し、パスで指定することもできません。

Key bindingsキー操作 #

The fuzzy finder uses skim’s default key bindings as they are.

KeyAction
TypingFilter incrementally
/ , Ctrl-p / Ctrl-n, Ctrl-k / Ctrl-jMove between candidates
Tab / Shift-TabToggle the selection of a candidate (multi-select mode only)
EnterConfirm
Shift-↑ / Shift-↓Scroll the preview
Esc / Ctrl-CAbort
  • Multi-select is available in cherry-pick / restore / add / stash push / commit / revert / status (file selection) / diff (file selection) / branch delete / branch cleanup / fetch --siblings / pull, and in the “inspect the conflicted files and mark them resolved” entry of the recovery menu. Pressing Enter without selecting anything with Tab targets the candidate under the cursor.
  • Entries that start out selected: gz commit (staged files), gz branch cleanup (every merged branch), gz fetch --siblings (the current repository) and gz pull (the current branch). All of them can be deselected with Tab.
  • The selection is always reordered to follow the candidate list before it is handed to git (the order skim returns is the order you picked, which is not necessarily the list order). Commands where the order matters each get their own direction: gz cherry-pick oldest first, gz revert newest first.
  • Aborting with Esc / Ctrl-C performs no git operation at all and exits with code 130.

fuzzy finder は skim の既定キーバインドをそのまま使います。

キー動作
文字入力インクリメンタルに絞り込む
/ Ctrl-p / Ctrl-nCtrl-k / Ctrl-j候補を移動する
Tab / Shift-Tab候補の選択を切り替える(複数選択モードのみ
Enter決定する
Shift-↑ / Shift-↓プレビューをスクロールする
Esc / Ctrl-C中断する
  • 複数選択に対応するのは cherry-pick / restore / add / stash push / commit / revert / status(ファイル選択)/ diff(ファイル選択)/ branch delete / branch cleanup / fetch --siblings / pull、および復帰メニューの 「コンフリクトファイルを確認して解決済みにする」です。Tab で 1 件も 選ばずに Enter を押した場合は、カーソル位置の候補が対象になります。
  • 起動時から選択済みになるのは gz commit(ステージ済みのファイル)、 gz branch cleanup(merged なブランチ全件)、 gz fetch --siblings(現在のリポジトリ)、gz pull (現在のブランチ)です。いずれも Tab で外せます。
  • 選択結果は常に候補一覧を基準に並べ直してから git へ渡します (skim が返す順序は選んだ順であり、候補の並び順とは限らないため)。 gz cherry-pick は古い順、gz revert は新しい順というように、 順序が意味を持つコマンドはそれぞれの向きへ揃えます。
  • Esc / Ctrl-C で中断した場合、git 操作は一切実行せず 終了コード 130 で終了します。

Exit codes終了コード #

CodeMeaning
0 Success (including gz status with no changes, gz diff with no differences, gz sync with behind 0 and gz worktree prune with nothing to prune)
1 Error (running outside a repository, zero candidates, a failing git command, and so on; the message goes to stderr). gz fetch --siblings returns 1 if even one repository fails to fetch (the remaining repositories still run to the end and are then summarised). gz pull likewise returns 1 if even one branch fails to integrate (the remaining branches still run to the end and are summarised; a failed branch is left in its pre-integration state).
2 Invalid command line (including bare gz and bare gz stash, where clap prints the help)
130 The fuzzy finder was aborted (Esc / Ctrl-C), or a confirmation prompt was declined. No git operation is performed

Run outside a git repository, fuzgit prints “Not a git repository. Run this command inside a git repository” and exits non-zero.

コード意味
0 正常終了(gz status の変更ゼロ、gz diff の差分ゼロ、gz sync の behind ゼロ、gz worktree prune の対象ゼロを含む)
1 エラー(リポジトリ外での実行、候補が 0 件、git コマンドの失敗など。メッセージは標準エラーへ)。 gz fetch --siblings は 1 件でも取得に失敗すると 1 になります (残りのリポジトリは最後まで実行したうえで集計します)。 gz pull も 1 件でも取り込みに失敗すると 1 になります (残りのブランチは最後まで実行したうえで集計します。失敗したブランチは取り込み前の状態のままです)
2 コマンドライン引数が不正(引数なしの gz / gz stash を含む。clap がヘルプを表示)
130 fuzzy finder の中断(Esc / Ctrl-C)、または確認プロンプトでの否認。git 操作は実行されない

git リポジトリ外で実行した場合は「git リポジトリではありません」と表示して非ゼロ終了します。

Out of scope / unsupportedスコープ外・非対応 #

Features deliberately left unimplemented. All of them can be done with plain git.

ItemReason
--rebase / --merge for gz pull (integration other than fast-forward) gz pull (fast-forwarding several branches at once) is implemented; what is not offered is the choice of method. Rebasing or merging a branch that is not checked out would require temporarily switching the working tree or creating a temporary worktree, both of which introduce risks that contradict the “never lose the user’s work” policy. Also, if the k-th of N branches conflicts, truly returning to the pre-pull state would mean rolling back the k-1 that already succeeded, which cannot be done without something as dangerous as git reset --hard. Integrating a single branch with an arbitrary method is covered by gz sync --rebase / --merge
--siblings / --prune / a positional branch name for gz pull --siblings would explode the number of selection combinations, and the “pick a branch per repository” model does not hold (pull is a write operation that advances local refs in somebody else’s repository, unlike gz fetch --siblings). --prune follows the policy that deleting tracking refs is stated explicitly via gz fetch --prune (same as gz sync). A positional argument is not provided because if you can type the name, plain git already suffices (the existing rule of skipping the finder when there is a single candidate already gives a near-non-interactive experience)
Pushing (gz push) There is nothing worth picking. The branch is fixed to the current one, and a repository almost always has a single remote, so the finder would open with one candidate every time. Plain git push already does the job. Force push is out of scope on top of that: destroying remote history cannot be guaranteed safe by a confirmation prompt alone
Reverting a merge commit (git revert -m) Picking the mainline requires understanding the history structure, and a fuzzy finder adds no value to that choice. When one is selected, fuzgit stops before running and points at plain git revert -m 1 <hash>
Running --dry-run inside the gz fetch preview A preview runs anew for every highlighted entry, so a network round trip would block the display. Updated refs are printed by git fetch itself when it runs
Recursive scanning, or a depth option (--depth etc.), for gz fetch --siblings One level below the parent directory already covers the use case; recursion would increase both the scan cost and the blast radius of network traffic. Repositories in deeper levels are not targeted
An option to specify the scan origin of gz fetch --siblings as a path Accepting an arbitrary path would break the assumption shared by every command that the current repository is the origin, and would let traffic be aimed at unintended directories. The origin is fixed to the parent of the worktree root
Per-repository remote selection in gz fetch --siblings It would mean repeating the finder once per repository, which breaks the operation model. Guessing a single remote implicitly is also avoided, in favour of the deterministic rule --all (every registered remote). Excluding a specific remote can be done on the git side with remote.<name>.skipFetchAll
Fetching sibling repositories in parallel git itself fetches multiple remotes sequentially by default. With inherited stdio the output would interleave and the destination of an authentication prompt would become unidentifiable (see Network operations)
Including bare repositories in the --siblings candidates There is no working tree for the fetched refs to land in. A bare repository such as foo.git falls outside the scan condition (the existence of <directory>/.git) and is never a candidate; a repository that has <directory>/.git but is configured bare is excluded and counted in the exclusion count shown in the header. This is a feature for the repositories you work in day to day
Following symlinked sibling directories The scan range is limited to real entries directly under the parent directory, preventing the set of contacted hosts from expanding unexpectedly
A [y/N] confirmation before gz fetch --siblings runs Making a multi-selection and pressing Enter is already an explicit decision, and a second confirmation is redundant. A fetch also does not damage local history (the scope of --prune is shown in the finder header)
Bulk operations other than fetch across sibling repositories (bulk status, bulk pull, etc.) fetch is the one operation suited to bulk execution: read-direction, non-destructive and safe to re-run. Extending this to other operations would need its own requirements
Custom signal handling during --siblings (e.g. Ctrl-C to skip the rest) With inherited stdio, SIGINT reaching the process group and terminating fuzgit with it is the expected behaviour. Custom control would be excessive
Automatic retry of failed repositories The existing policy of adding no timeouts and no retries continues. The names of failed repositories appear in the summary line, so the user can decide about re-running
tag vs tag in gz diff Rarely needed; tag vs HEAD is available as gz tag --diff
gz worktree move / lock / unlock / repair, and remove --force Rarely needed day to day, and each is a single plain git command. Discarding uncommitted changes is hard to justify behind a confirmation prompt alone
A text input UI for branch names and worktree paths skim has no text input UI. Names and paths are positional arguments (gz branch create <name> / gz worktree add <path>) and the fuzzy selection is limited to the starting point / the branch. gz worktree add also never suggests a directory name automatically
Hunk-level partial commit / stage File-level selection covers the use case. Hunk selection does not fit skim’s candidate model (one line = one item)
Running the autosquash rebase automatically after gz fixup History rewriting is never run automatically; the steps are printed to stderr and nothing more
A recovery menu for cherry-pick / revert / fetch The recovery menu is limited to merge / rebase. Conflicts in cherry-pick / revert are left to git’s standard message and its --continue / --abort guidance
“Open the conflicted file in an editor” That would launch $EDITOR directly without going through git, which needs a separate design decision to stay consistent with the no-shell policy

意図的に実装していない機能です。いずれも素の git で実行できます。

項目理由
gz pull--rebase / --merge(fast-forward 以外の取り込み方式) gz pull(複数ブランチを fast-forward で一括取り込み)は実装済み。 方式を選べるようにしないのは、チェックアウトしていないブランチを rebase / merge するには 作業ツリーを一時的に切り替えるか一時 worktree を作るしかなく、いずれも 「ユーザーの作業を失わせない」方針に反するリスクを持ち込むため。また N 本中 k 本目で コンフリクトした場合、真に「pull 前」へ戻すには成功済みの k-1 本も巻き戻す必要があり、 git reset --hard 相当の危険な操作なしには成立しない。単一ブランチを任意の 方式で取り込む用途は gz sync --rebase / --merge が満たす
gz pull--siblings / --prune / ブランチ名の位置引数 --siblings は選択の組み合わせが爆発し、リポジトリごとにブランチを選ぶ 操作モデルが成立しない(pull は他人のリポジトリのローカル参照を進める書き込み操作であり、 gz fetch --siblings と性質が異なる)。--prune は追跡参照の削除を gz fetch --prune として明示指定する方針(gz sync と同じ)。 位置引数は、名前を打てるなら素の git で足りるため設けない(候補が 1 件なら finder を 省略する既存原則で非対話に近い使い勝手は確保される)
push(gz push 選ぶ価値のある軸が無い。対象ブランチは現在のブランチに固定され、リモートは 1 つだけのリポジトリがほとんどであるため、候補 1 件の finder が毎回開くだけになる。素の git push で足りる。加えて force push は、リモート履歴の破壊を確認プロンプトだけでは担保できないためスコープ外
マージコミットの revert(git revert -m mainline の選択は履歴構造の理解を要し、fuzzy finder で「選ぶ」価値が乗らない。 選択された場合は実行前に停止し、素の git revert -m 1 <hash> を案内する
gz fetch のプレビューでの --dry-run 実行 プレビューは選択項目ごとに都度実行されるため、ネットワーク往復が描画をブロックする。更新された参照は git fetch 自身が実行時に表示する
gz fetch --siblings の再帰探索・探索深度の指定(--depth 等) 親ディレクトリ直下の 1 階層で用途を満たせており、再帰は走査コストとネットワークの影響範囲を同時に増やす。深い階層のリポジトリは対象にしない
gz fetch --siblings の探索起点をパスで指定するオプション 任意パスを受け取ると「現在のリポジトリを起点にする」という全コマンド共通の前提が崩れ、意図しないディレクトリへ通信を飛ばせてしまう。起点はワークツリー root の親に固定する
gz fetch --siblings でのリポジトリごとのリモート選択 リポジトリ数だけ finder を繰り返すことになり操作モデルが成立しない。暗黙に 1 つのリモートを 推測するのも避け、--all(登録済みの全リモート)という決定的な規則を採る。 特定リモートの除外は git 側の remote.<name>.skipFetchAll で行える
兄弟リポジトリの並列 fetch git 自身も複数リモートの fetch を既定で逐次実行する。継承 stdio では出力が混ざり、認証プロンプトの宛先も判別できなくなる(ネットワーク操作について参照)
bare リポジトリを --siblings の候補に含めること fetch した参照の反映先となる作業ツリーが無い。foo.git のような bare は 走査条件(<ディレクトリ>/.git の存在)から外れて候補にならず、 <ディレクトリ>/.git を持つが bare 設定のリポジトリは除外件数に数えて ヘッダーへ示す。日常的に作業するリポジトリを対象とする機能である
シンボリックリンクの兄弟ディレクトリを辿ること 走査範囲を「親ディレクトリ直下の実体」に限定し、通信先が予期せず広がることを防ぐ
gz fetch --siblings 実行前の [y/N] 確認プロンプト 複数選択して Enter を押すこと自体が明示的な決定であり、二重確認は冗長。fetch はローカル履歴を壊さない操作でもある(--prune の適用範囲は finder のヘッダーで示す)
兄弟リポジトリへの fetch 以外の一括操作(一括 status / 一括 pull 等) fetch は「読み取り方向・非破壊・失敗しても再実行できる」唯一の一括実行に適した操作である。他の操作へ広げるのは別途要件から起こす
--siblings 実行中の独自シグナルハンドリング(Ctrl-C で残りをスキップ等) 継承 stdio のままプロセスグループへ SIGINT が届き fuzgit ごと終了するのが期待挙動。独自制御は過剰
失敗したリポジトリの自動リトライ fuzgit 側でタイムアウト・リトライを行わない既存方針を継続する。失敗したリポジトリ名は集計行に出るため、再実行の判断はユーザーが行える
gz diff の tag vs tag 使用頻度が低く、tag vs HEAD は gz tag --diff で行える
gz worktree の move / lock / unlock / repair、remove --force 日常頻度が低く、いずれも素の git で 1 コマンドで済む。未コミット変更の破棄は確認プロンプトだけでは担保しにくい
ブランチ名・worktree パスのテキスト入力 UI skim にテキスト入力 UI は無い。名前・パスは位置引数 (gz branch create <name> / gz worktree add <path>)とし、 fuzzy 選択は作成元・ブランチに限定する。gz worktree add のディレクトリ名の 自動提案も行わない
hunk 単位の部分 commit / stage ファイル単位の選択で用途を満たす。hunk 選択は skim の候補モデル(行=項目)に乗らない
gz fixup 後の autosquash rebase の自動実行 履歴改変の自動実行は行わない。手順を標準エラーへ表示するに留める
cherry-pick / revert / fetch の復帰メニュー 復帰メニューは merge / rebase に限定する。cherry-pick / revert のコンフリクトは git の標準メッセージと --continue / --abort の案内に委ねる
コンフリクトファイルを「エディタで開く」 git を介さず $EDITOR を直接起動する経路になり、シェル非経由方針との整合に別途設計判断が必要

Debuggingデバッグ #

$ FUZGIT_DEBUG=1 gz branch
[fuzgit] (B) git log --color=always --oneline --decorate -n 50 feature -- [LANGUAGE=en]
[fuzgit] (B) git switch feature [LANGUAGE=en]

$ FUZGIT_DEBUG=1 gz status
[fuzgit] (A) (cwd: .) git status --porcelain -z --untracked-files=all [LC_MESSAGES=C]

Setting the environment variable FUZGIT_DEBUG=1 prints every git command fuzgit runs to stderr.

  • It is enabled only when the value is exactly 1. Unset, empty, 0, true and anything else are all disabled.
  • The destination is always stderr (stdout is kept free for piping hashes, tag names and paths).
  • The log lists the argument array as-is. Because it is printed on every preview generation, it can visibly mix with the drawing while the fuzzy finder is on screen.
  • Commands that target another directory are printed as [fuzgit] (A|B) (cwd: <directory>) git ....
  • (A) marks a call whose output fuzgit parses: it always runs with LC_MESSAGES=C so parsing cannot depend on the environment. (B) marks a call whose output you read (inherited stdio, previews, reports passed through): the resolved display language is propagated as LANGUAGE=<ja|en>. The variables that were set or removed are listed in brackets at the end of the line, so the log answers “which language did this git speak?” directly. See Language.

環境変数 FUZGIT_DEBUG=1 を指定すると、fuzgit が実行した git コマンドを 標準エラーへ出力します。

  • 有効になるのは値が厳密に 1 の場合だけです。未設定・空文字・0true などはすべて無効です。
  • 出力先は必ず標準エラーです(標準出力はハッシュ・タグ名・パスのパイプ用途のために空けてあります)。
  • ログにはコマンドの引数配列がそのまま並びます。プレビュー生成のたびに出力されるため、fuzzy finder の表示中は画面の描画と混ざって見えることがあります。
  • 別ディレクトリを対象に実行するコマンドは [fuzgit] (A|B) (cwd: <ディレクトリ>) git ... の形式で出力します。
  • (A)fuzgit が出力をパースする呼び出しで、実行環境に よってパースが壊れないよう常に LC_MESSAGES=C で実行します。 (B)ユーザーが出力を読む呼び出し(継承 stdio・プレビュー・ そのまま提示する報告)で、解決された表示言語を LANGUAGE=<ja|en> として伝播します。設定・削除した環境変数は 行末の角括弧に併記されるため、「この git は何語で話したのか」がログから直接 分かります。表示言語も参照してください。

Development開発 #

Quality gate品質ゲート

Confirm that all of these succeed, in this order, on every change.

以下の順にすべて成功することを、変更のたびに確認してください。

cargo build
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test

Testing policyテスト方針

  • Interactive paths that take over the terminal (the TUI) are outside the scope of automated tests and are verified manually.
  • Automated tests cover pure logic (formatting candidates, assembling git arguments, parsing git status / git stash list / git worktree list) as unit tests, and non-interactive paths (--help, running outside a repository, zero candidates) as integration tests with assert_cmd.
  • Paths involving the network (gz fetch / gz pull / gz sync) are not automated; only their non-interactive paths (no remotes, no upstream, zero candidates and similar errors) are verified. Real-world checks use a local bare repository as the remote and never connect to an external network.
  • 端末(TUI)を占有する対話パスは自動テストの対象外とし、手動確認とします。
  • 自動テストの対象は、候補の整形・git 引数の組み立て・git status / git stash list / git worktree list のパースなどの純ロジック (単体テスト)と、--help・リポジトリ外実行・候補 0 件などの非対話パス (assert_cmd による統合テスト)です。
  • ネットワークを伴う経路(gz fetch / gz pull / gz sync)は自動テスト化せず、非対話パス(リモートゼロ・upstream 未設定・ 候補ゼロなどのエラー)のみを検証します。実機確認はローカルの bare リポジトリを remote に設定して行い、外部ネットワークへは接続しません。

Design principles設計方針

  • Read with gix, write by shelling out to git. gix does not provide the write operations for switch / cherry-pick / restore / add / stash, so they are delegated to the system git in order to keep behavioural compatibility. The worktree listing is read with git worktree list --porcelain -z rather than gix (gix cannot handle the main worktree or the checked-out branch).
  • External commands always run as Command::new("git").args([...]), i.e. with an argument array, never through a shell. Paths that come from the user are passed after -- as the pathspec :(top,literal)<path>, removing any room for them to be interpreted as options or wildcards.
  • Previews do not hand skim a shell command string; git is executed from Rust and the result is displayed, which structurally prevents injection through candidate strings.
  • Positional arguments that cannot be protected with --, such as branch names and commit hashes (switch / merge / rebase / push / fixup / revert / diff), are passed to git only after checking that the selection is present in the candidate list. The only values that get through originate from real refs enumerated by gix. gz sync and gz pull verify that branch.<name>.remote is a registered remote name before running git fetch (the configuration can hold a URL directly, so without the check they could connect to something that was never enumerated). The refspec gz pull uses for branches that are not checked out carries no forcing +, so git rejects non-fast-forward updates and local commits cannot be lost.
  • No network access during candidate generation or previews.
  • The only command that touches anything outside the current repository is gz fetch --siblings. The scan origin is always derived from the current repository’s worktree root (no path argument is accepted) and is limited to one level under the parent directory. Sibling repositories are opened with gix::open, not gix::discover (discover walks upwards, so a directory without a .git would grab a repository above the scan range). The selection is checked for an exact match against the candidate list, using normalised absolute paths, before it becomes a target.
  • Destructive operations (discarding changes with restore, stash drop, branch delete / cleanup, history rewriting with rebase, the integration performed by sync, worktree remove / prune, and aborting a merge / rebase) are confirmed first. merge is confirmed too, showing how many commits will come in and the conflict prediction.
  • History rewriting is never run automatically (gz fixup only prints the autosquash steps; it does not rebase).
  • Pushing is not provided at all, so neither is force push. Use plain git push.
  • 読み取りは gix、書き込みは git へのシェルアウト。 gix は switch / cherry-pick / restore / add / stash の書き込み操作を提供していないため、 挙動互換性を優先してシステムの git に委譲しています。worktree の一覧だけは gix ではなく git worktree list --porcelain -z を読みます(gix は main worktree と チェックアウト中のブランチを扱えないため)。
  • 外部コマンドは常に Command::new("git").args([...])引数配列渡しで 実行し、シェルを一切経由しません。ユーザー由来のパスは -- の後ろに :(top,literal)<path> のパススペックとして渡し、オプションや ワイルドカードとして解釈される余地を排除しています。
  • プレビューは skim にシェルコマンド文字列を渡す方式ではなく、Rust 側で git を実行した 結果を表示する方式を採り、候補文字列由来のインジェクションを構造的に防いでいます。
  • ブランチ名・コミットハッシュのように -- で保護できない位置引数 (switch / merge / rebase / push / fixup / revert / diff)は、 選択結果が候補一覧に含まれることを確かめてから git へ渡します。 渡る値は gix が列挙した実在の参照に由来するものだけです。gz syncgz pullbranch.<name>.remote が登録済みのリモート名で あることを検証してから git fetch します(設定には URL を直接書けるため、 検証しないと列挙していない対象へ接続することになります)。gz pull が チェックアウトしていないブランチを更新する refspec には、強制更新の + を 付けません(非 fast-forward の更新を git に拒否させ、ローカルのコミットを 失わせないためです)。
  • 候補生成・プレビューでネットワークアクセスを行いません。
  • 現在のリポジトリ以外に触れるのは gz fetch --siblings だけです。 走査の起点は常に現在のリポジトリのワークツリー root から導出し(パス引数を受け取りません)、 親ディレクトリ直下の 1 階層に限定します。兄弟のリポジトリを開くのは gix::open であり gix::discover ではありません (discover は上位へ遡るため、.git を持たないディレクトリから 走査範囲より上のリポジトリを掴んでしまいます)。選択結果は正規化済みの絶対パスで 候補一覧と完全一致することを確かめてから実行対象にします。
  • 破壊的操作(restore による変更破棄、stash dropbranch delete / cleanuprebase による履歴改変、 sync の取り込み、worktree remove / prune、 merge / rebase の abort)は実行前に確認を挟みます。merge にも 確認を設け、取り込まれるコミット数とコンフリクト予測を提示します。
  • 履歴改変の自動実行は行いません(gz fixup は autosquash の手順を表示するだけで rebase しません)。
  • push 自体を提供しないため、force push も提供しません。素の git push を使ってください。

Module layoutモジュール構成

src/
├── main.rs          # entry point (parse the CLI, dispatch, exit code)
├── lib.rs
├── cli.rs           # command definitions via clap derive
├── error.rs         # domain errors via thiserror
├── finder.rs        # skim wrapper (candidates, preview, abort detection)
├── git/
│   ├── repo.rs      # opening the repository with gix
│   ├── read.rs      # reading candidate data via gix / git
│   ├── siblings.rs  # scanning repositories under the parent directory (gz fetch --siblings only)
│   └── exec.rs      # running git (run_git / capture_git) and the debug log
└── commands/        # the use case implementation of each subcommand
src/
├── main.rs          # エントリポイント(CLI パース → dispatch → 終了コード)
├── lib.rs
├── cli.rs           # clap derive によるコマンド定義
├── error.rs         # thiserror によるドメインエラー
├── finder.rs        # skim ラッパー(候補・プレビュー・中断判定)
├── git/
│   ├── repo.rs      # gix によるリポジトリのオープン
│   ├── read.rs      # gix / git による候補データの読み取り
│   ├── siblings.rs  # 親ディレクトリ直下のリポジトリ探索(gz fetch --siblings 専用)
│   └── exec.rs      # git の実行(run_git / capture_git)とデバッグログ
└── commands/        # 各サブコマンドのユースケース実装

Licenseライセンス #

MIT License. The full text is in LICENSE.

MIT License. 全文は LICENSE を参照してください。