ツール 2025-06-15

Go製Git操作ツール『ggc』の紹介

ggcの全機能解説。CLI/インタラクティブ分離アーキテクチャ、Fuzzyサーチエンジンの実装、Workflow Mode内部構造、カスタマイズ可能なエイリアス、階層型キーバインドプロファイルシステム。

Read in: en
Go製Git操作ツール『ggc』の紹介

Go製Git操作ツール『ggc』の紹介

ggcとは

ggcはGo製のGitワークフローツールだ。日常的なGitサブコマンドを一貫したインターフェースで提供し、コマンド名を暗記せずとも操作できるインタラクティブFuzzy検索TUIを搭載する。Workflow Mode、プレースホルダー対応のカスタムエイリアス、階層型キーバインドプロファイルシステムなど多くの機能を持つ。本記事ではその全てを解説する。Awesome Goにも掲載されているツールだ。

デモ

ブランチ管理 CLIワークフロー インタラクティブ概要
ブランチ管理デモ CLIワークフローデモ インタラクティブ概要デモ

ggcを使うメリット

コマンドを覚えなくていい

Gitには200以上のサブコマンドがある。日常的に使うもの以外は「stash applyのIDの書き方がわからない」「amend --no-editのフラグ名を思い出せない」と手が止まる。ggcのインタラクティブモードなら、bdと打つだけでbranch deleteが絞り込まれ、cacommit amendも見つかる。探索コストがゼロになる。

代表的なユースケース

毎日のadd → commit → push: Workflow Modeで3コマンドをキューに積んでおけば、次回以降はxを押すだけで完結する。コミットメッセージはプレースホルダーとして実行時に入力できるため、毎回ワークフローを組み直す必要はない。

ブランチ管理: branch checkout remoteでリモートから追跡ブランチを作成、branch delete mergedでマージ済みブランチを一括削除など、複合操作をわかりやすい名前のコマンドとして提供している。gitのフラグを調べる必要がない。

Fixupワークフロー: commit fixup <commit>rebase autosquashのシーケンスをエイリアスに登録しておけば、fixupコミットの適用をコマンド1つで実行できる。

hookの管理: hook list / hook enable / hook disableでプロジェクトのGit hookをGUIなしで手軽に管理できる。

他のGitツールとの比較

ツール 方式 強み ggcとの違い
git(素) CLI 完全な機能・フラグで細かく制御 コマンド名・フラグの記憶が必要
lazygit TUI専用 豊富なパネルUI TUIのみ、スクリプトへの組み込みが難しい
tig TUI(読み取り) ログ・差分の視覚化 操作機能なし
gh CLI GitHub連携 ローカルGit操作は対象外
gitエイリアス CLI 完全にカスタム可能 発見性なし・全部自分で書く必要あり

ggcの強みはCLI/TUIを自由に使い分けられることだ。スクリプトからはggc push forceをそのまま呼び出せる。インタラクティブに使いたければggcと打つだけでfuzzy検索が起動する。どちらのパスも同じRoute()で処理するため挙動が一致する。

CLI/インタラクティブモードのアーキテクチャ

ggcには2つの実行パスがある。直接コマンドを実行するCLIパスと、フルスクリーンTUIを起動するインタラクティブパスだ。cmd/execute.goExecute()メソッドがどちらのパスを連るかを決定する唯一のエントリーポイントだ。

func (c *Cmd) Execute(args []string) error {
    if len(args) == 0 {
        c.Interactive()
        return nil
    }

    cmdName, cmdArgs := args[0], args[1:]

    // エイリアスかチェック
    if c.configManager != nil && c.configManager.GetConfig().IsAlias(cmdName) {
        return c.executeAlias(cmdName, cmdArgs)
    }

    // 通常コマンド
    return c.Route(args)
}

引数がゼロの場合はInteractive()でTUIを起動する。エイリアス名を検出した場合はexecuteAlias()で展開し実行する。それ以外はRoute()に渡す。このRoute()関数はCLIパス、エイリアス展開、Workflow実行エンジンの3つすべてが共有する実装だ。

インタラクティブモードの設計

インタラクティブTUIは1つのターミナル画面を共有する2つのサブモードに分かれている。Ctrl+tでトグルする。

Search Modeがデフォルトだ。全コマンドの一覧を表示し、入力に応じてリアルタイムにフィルタリングする。スコアリングはinternal/interactive/fuzzy.gomatchPattern()関数が担当する。

func matchPattern(textRunes, patternRunes []rune) (bool, matchMetadata) {
    meta := matchMetadata{firstIndex: -1, lastIndex: -1}
    textIdx := 0
    patternIdx := 0

    for textIdx < len(textRunes) && patternIdx < len(patternRunes) {
        if textRunes[textIdx] == patternRunes[patternIdx] {
            if meta.firstIndex == -1 {
                meta.firstIndex = textIdx
            }
            if meta.lastIndex != -1 {
                meta.gapScore += textIdx - meta.lastIndex - 1
            }
            meta.lastIndex = textIdx
            patternIdx++
        }
        textIdx++
    }

    if patternIdx != len(patternRunes) {
        return false, meta
    }
    return true, meta
}

アルゴリズムは古典的な部分列マッチングだ。パターンの全文字がテキスト内に順序通り現れればマッチと判定する(連続不要)。matchMetadata構造体は3つのシグナル—firstIndex(マッチ開始位置)、gapScore(文字間の距離合計)、lastIndex—を蓄積し、matchScoreとしてソートに利用する。致密で早い位置のマッチが、散在した遅いマッチより高くランク付けされる。

Workflow Mode

Ctrl+tを押すとinternal/interactive/ui_mode.goToggleWorkflowView()が呼び出される。

func (ui *UI) ToggleWorkflowView() {
    if ui.state.IsWorkflowMode() {
        ui.enterSearchMode()
        return
    }
    ui.enterWorkflowMode()
}

Workflow Modeでは、検索リストの任意アイテムでTabを押すことでggcコマンドのシーケンス(例:addcommitpush)を組み立てられる。xを押すとWorkflowExecutor.Execute()が順次実行する。

func (we *WorkflowExecutor) Execute(workflow *Workflow) error {
    steps := workflow.GetSteps()
    if len(steps) == 0 {
        return fmt.Errorf("workflow is empty")
    }

    for i, step := range steps {
        resolvedArgs, canceled := resolveStepPlaceholders(we.ui, step)
        if canceled {
            return ErrWorkflowCanceled
        }

        parts := append([]string{step.Command}, resolvedArgs...)
        if err := we.router.Route(parts); err != nil {
            return fmt.Errorf("step %d/%d failed: %w", i+1, len(steps), err)
        }
    }
    return nil
}

各ステップの前にresolveStepPlaceholders()が引数内の<name>トークンをスキャンし、存在する場合はインタラクティブに入力を求める。事前にコミットメッセージをハードコードする必要はなく、実行時にプロンプトで單やかに入力できる。

利用可能なコマンド一覧

ggcが提供する全コマンドのリファレンスだ。CLIから直接呼び出せるほか、インタラクティブモードのFuzzy検索で同じ名前を入力して検索できる。

コマンド 説明
add . すべての変更をインデックスに追加
add <file> 指定ファイルをインデックスに追加
add interactive インタラクティブに変更を追加
add patch インタラクティブに変更を追加(パッチモード)
help メインヘルプメッセージを表示
help <command> 指定コマンドのヘルプを表示
reset origin/<ブランチ>へハードリセットし作業ディレクトリをクリーン
reset hard <commit> 指定コミットへハードリセット
reset soft <commit> ソフトリセット:HEADを移動し変更はステージに保持
branch checkout 既存ブランチへ切り替え
branch checkout remote リモートからローカル追跡ブランチを作成してチェックアウト
branch contains <commit> 指定コミットを含むブランチを表示
branch create 新しいブランチを作成してチェックアウト
branch current 現在のブランチ名を表示
branch delete ローカルブランチを削除
branch delete merged マージ済みローカルブランチを削除
branch info <branch> ブランチの詳細情報を表示
branch list local ローカルブランチを一覧表示
branch list remote リモートブランチを一覧表示
branch list verbose ブランチの詳細一覧を表示
branch move <branch> <commit> ブランチを指定コミットへ移動
branch rename <old> <new> ブランチをリネーム
branch set upstream <branch> <upstream> ブランチのアップストリームを設定
branch sort [date|name] 日付またはアルファベット順にブランチを一覧表示
commit <message> メッセージ付きでコミット作成
commit allow empty 空コミットを作成
commit amend 前のコミットを修正(エディタ起動)
commit amend no-edit コミットメッセージを編集せずに修正
commit fixup <commit> 指定コミットを対象にfixupコミットを作成
log graph グラフ付きでログを表示
log simple シンプルな履歴ログを表示
fetch リモートからフェッチ
fetch prune フェッチして古い参照を削除
pull current 現在のブランチをリモートからプル
pull rebase プルしてリベース
push current 現在のブランチをリモートへプッシュ
push force 現在のブランチを強制プッシュ
remote add <name> <url> リモートリポジトリを追加
remote list すべてのリモートリポジトリを一覧表示
remote remove <name> リモートリポジトリを削除
remote set-url <name> <url> リモートURLを変更
status 作業ツリーの状態を表示
status short 簡潔なステータスを表示(porcelain形式)
clean dirs 追跡されていないディレクトリを削除
clean files 追跡されていないファイルを削除
clean interactive インタラクティブにファイルを削除
restore . 作業ディレクトリのすべてのファイルをインデックスから復元
restore <commit> <file> 指定コミットからファイルを復元
restore <file> 作業ディレクトリのファイルをインデックスから復元
restore staged . すべてのファイルをアンステージ
restore staged <file> ファイルをアンステージ(HEADからインデックスへ復元)
diff 変更を表示(git diff HEAD)
diff head HEADとの差分を表示
diff staged ステージ済みの変更を表示
diff unstaged ステージされていない変更を表示
tag annotated <tag> <message> 注釈付きタグを作成
tag create <tag> タグを作成
tag delete <tag> タグを削除
tag list すべてのタグを一覧表示
tag push タグをリモートへプッシュ
tag show <tag> タグ情報を表示
config get <key> 設定値を取得
config list すべての設定を一覧表示
config set <key> <value> 設定値をセット
hook disable <hook> フックを無効化
hook edit <hook> フックの内容を編集
hook enable <hook> フックを有効化
hook install <hook> フックをインストール
hook list すべてのフックを一覧表示
hook uninstall <hook> 既存のフックをアンインストール
rebase <upstream> 現在のブランチを<upstream>にリベース
rebase abort 進行中のリベースを中止
rebase autosquash --autosquashオプション付きインタラクティブリベース
rebase continue 進行中のリベースを継続
rebase interactive インタラクティブリベース
rebase skip 現在のパッチをスキップして継続
stash 現在の変更をスタッシュ
stash apply スタッシュを削除せずに適用
stash apply <stash> 指定スタッシュを削除せずに適用
stash branch <branch> スタッシュからブランチを作成
stash branch <branch> <stash> 指定スタッシュからブランチを作成
stash clear すべてのスタッシュを削除
stash create スタッシュを作成してオブジェクト名を返す
stash drop 最新のスタッシュを削除
stash drop <stash> 指定スタッシュを削除
stash list すべてのスタッシュを一覧表示
stash pop 最新のスタッシュを適用して削除
stash pop <stash> 指定スタッシュを適用して削除
stash push 変更を新しいスタッシュに保存
stash push -m <message> メッセージ付きで変更を新しいスタッシュに保存
stash save <message> メッセージ付きで変更を新しいスタッシュに保存
stash show スタッシュ内の変更を表示
stash show <stash> 指定スタッシュ内の変更を表示
stash store <object> スタッシュオブジェクトを保存
debug-keys 現在のキーバインドを表示
debug-keys raw ターミナルのキーシーケンスをインタラクティブにキャプチャ
debug-keys raw <file> キーシーケンスをキャプチャしてファイルに保存
quit インタラクティブモードを終了
version ggcの現在バージョンを表示

その他の機能

YAMLによるWorkflow事前定義

エイリアスとは別に、~/.ggcconfig.yamlworkflows:セクションにワークフローを事前登録できる。TUI起動時に自動でWorkflow Modeに読み込まれ、nキーで作成したワークフローと并列して管理できる。

workflows:
  daily:
    - "add ."
    - "commit -m <message>"
    - "push current"
  deploy:
    - "branch checkout <branch>"
    - "pull current"
    - "push <branch>"

各ステップの<name>トークンは実行時にプロンプトで入力を求める。xで実行するとその場でメッセージやブランチ名を入力できる。エイリアス(CLIから呼び出す短縮形)との違いは、Workflow Modeで複数コマンドを順次定義・再利用する点にある。

コマンドエイリアス

エイリアスは~/.ggcconfig.yamlで定義する。シンプル形式とシーケンス形式の両方をサポートする。

aliases:
  br: branch          # シンプル — ggc br list
  ci: commit

  quick:              # シーケンス
    - status
    - add .
    - commit

  deploy:             # プレースホルダー付きシーケンス
    - "branch checkout {0}"
    - "pull current"
    - "push {0}"

キーバインドプロファイル

インタラクティブUIは4つのビルトインキーバインドプロファイル(defaultemacsvireadline)をサポートする。キーバインドは6層の優先順に解決される—デフォルト → プロファイル → プラットフォーム → ターミナル → ユーザー設定 → 環境変数オーバーライド。

interactive:
  profile: emacs
  keybindings:
    move_up: "ctrl+p"
    move_down: "ctrl+n"
    toggle_workflow_view: "ctrl+t"
    add_to_workflow: "tab"

クロスプラットフォーム対応

GoReleaserを介してLinux、macOS、Windows(amd64 / arm64 / 386)すべてのpresetバイナリを配布している。インタラクティブTUIの全機能が3プラットフォームで動作する。

シェル補完

ggcはBash、Zsh、Fishのシェル補完スクリプトを同梱している。tools/completions/に生成済みスクリプトが収録されており、make completionsでコマンドレジストリから再生成できる。

# Bash(~/.bash_profile または ~/.bashrc に追記)
if [ -f ~/.ggc-completion.bash ]; then
  . ~/.ggc-completion.bash
fi

# Zsh(~/.zshrc に追記)
if [ -f ~/.ggc-completion.zsh ]; then
  . ~/.ggc-completion.zsh
fi

# Fish(~/.config/fish/config.fish に追記)
if test -f ~/.ggc-completion.fish
    source ~/.ggc-completion.fish
end

有効化するとggc b<Tab>branchに補完され、ggc branch <Tab>でサブコマンド一覧が展開される。

統一構文と--セパレーター

ggcは-x/--long形式のフラグを持たない統一構文を採用している。全操作はスペース区切りのサブコマンドで表現する(例: ggc fetch pruneggc commit allow empty)。--以降の引数はコマンドではなくデータとして扱われるため、先頭に-が付く文字列も安全に渡せる。

# 先頭に - がある引数を渡す場合
ggc commit -- "-fix leading dash"

この設計はCLIの挙動を予測可能にし、スクリプトへの埋め込みを容易にする。

Soft cancel

インタラクティブモードを終了せず、現在の操作を中断してSearch Modeに戻るにはCtrl+G(または特殊文字が続かないEsc)を使う。Ctrl+Cがインタラクティブモードごと終了するのに対し、Ctrl+Gはモードを維持したまま検索画面に戻る。

debug-keysコマンド

キーバインドの動作確認やトラブルシューティングに使える組み込みコマンドだ。

# 現在のキーバインド設定を一覧表示
ggc debug-keys

# ターミナルが送出するキーシーケンスをリアルタイムでキャプチャ
ggc debug-keys raw

# キーシーケンスをファイルに保存
ggc debug-keys raw keydump.txt

ggc debug-keys rawを実行してキーを押すと、ターミナルが実際に送信しているバイト列が表示される。キーバインドが期待どおりに動作しない場合の原因特定に役立つ。

tmuxサポート

tmux環境でキー入力が正しく処理されない場合、.tmux.confに以下を追加する。

set -g xterm-keys on

インストール

# Homebrew (macOS/Linux)
brew install ggc

# インストールスクリプト(最もかんたん)
curl -sSL https://raw.githubusercontent.com/bmf-san/ggc/main/install.sh | bash

# Go install
go install github.com/bmf-san/ggc/v8@latest

まとめ

ggcは、明確な設計思想に基づく柔軟なGitワークフローツールだ。CLIパスとインタラクティブパスは同じRoute()層を共有しており、挙動が一貫している。Fuzzyサーチスコアラーは依存性ゼロの純粋関数であり、Workflow実行エンジンはコマンドディスパッチロジックを重複定義せずにRoute()を再利用する。

Tags: Golang Git CLI TUI
Share: 𝕏 Post Facebook Hatena
✏️ View source / Discuss on GitHub
☕ サポート

このブログを応援していただける方は、以下からサポートをお願いします。いただいたサポートはブログ運営・技術研鑽に活用します。


関連記事