😸

リポジトリを綺麗に管理 ghq+fzf

に公開

はじめに

開発者として複数のGitリポジトリを扱う際、効率的な管理方法が求められます。本記事では、GitHub CLI(gh)、ghq、fzfを組み合わせた強力なリポジトリ管理ワークフローを紹介します。

この組み合わせにより、以下のことが可能になります:

  • リポジトリの一元管理
  • 高速なリポジトリ検索と移動
  • インタラクティブなリポジトリ選択
  • 危険な操作も安全に実行

必要なツール

ghq

Gitリポジトリを統一された場所で管理するツール。リポジトリを~/ghq/github.com/owner/repoのような構造で整理します。

GitHub CLI (gh)

GitHubのAPIを使用してリポジトリ情報を取得したり、操作を実行できるコマンドラインツール。

fzf

インタラクティブなファジーファインダー。リストから項目を選択する際の強力なUIを提供します。

Installation

必要なツールをインストールします。

Ubuntu/Debian

sudo apt install ghq gh fzf

macOS

brew install ghq gh fzf

Arch Linux

sudo pacman -S ghq github-cli fzf

基本的な使用方法

1. リポジトリの一覧表示と選択

GitHubのリポジトリ一覧を取得し、fzfで選択できます:

gh api --paginate /user/repos --jq '.[].full_name' | fzf

このコマンドは以下の処理を行います:

  • gh api --paginate /user/repos:GitHub APIからユーザーのリポジトリ一覧を取得
  • --jq '.[].full_name':JSON形式の結果からowner/repo形式のリポジトリ名を抽出
  • | fzf:リストをfzfに渡してインタラクティブに選択

2. リポジトリのクローンと移動

ghqを使用してリポジトリをクローンし、移動する:

# リポジトリをクローン
ghq get https://github.com/owner/repo

# ghqで管理されているリポジトリに移動
cd $(ghq root)/github.com/owner/repo

# またはghqのlistとfzfを組み合わせて選択
cd $(ghq root)/$(ghq list | fzf)

3. 実用的なワークフロー

以下のような組み合わせが効果的です:

# 自分のリポジトリから選択してクローン
repo=$(gh api --paginate /user/repos --jq '.[].clone_url' | fzf)
ghq get $repo

# クローン済みリポジトリから選択して移動
cd $(ghq root)/$(ghq list | fzf)

高度な使用例

リポジトリの削除(危険操作)

⚠️ 注意: このコマンドは実際にリポジトリを削除します。実行前に必ず確認してください。

gh repo delete $(gh api --paginate /user/repos --jq '.[].full_name' | fzf)

エイリアスの設定

よく使用するコマンドはエイリアスとして設定しておくと便利です:

# ~/.bashrc または ~/.zshrc に追加
alias ghr="gh api --paginate /user/repos --jq '.[].full_name' | fzf"
alias ghq-cd="cd \$(ghq root)/\$(ghq list | fzf)"

リポジトリ管理ツール repo()

repo() {
    local action="${1:-list}"
    
    case "$action" in
        "list"|"l")
            # List all repositories with fzf selection
            local selected_repo
            selected_repo=$(ghq list | fzf --height=50% --border --preview="echo {}" --preview-window=down:3:wrap)
            if [[ -n "$selected_repo" ]]; then
                echo "Selected: $selected_repo"
                echo "Path: $(ghq root)/$selected_repo"
            fi
            ;;
        "cd"|"c")
            # Change directory to selected repository
            local selected_repo
            selected_repo=$(ghq list | fzf --height=50% --border --preview="echo {}" --preview-window=down:3:wrap)
            if [[ -n "$selected_repo" ]]; then
                cd "$(ghq root)/$selected_repo" || return 1
            fi
            ;;
        "remove"|"rm"|"r")
            # Remove selected repository
            local selected_repo
            selected_repo=$(ghq list | fzf --height=50% --border --preview="echo {}" --preview-window=down:3:wrap --prompt="Select repository to remove: ")
            if [[ -n "$selected_repo" ]]; then
                echo "Are you sure you want to remove $selected_repo? [y/N]"
                read -r confirm
                if [[ "$confirm" =~ ^[Yy]$ ]]; then
                    rm -rf "$(ghq root)/$selected_repo"
                    echo "Removed: $selected_repo"
                else
                    echo "Cancelled"
                fi
            fi
            ;;
        "get"|"g")
            # Clone/get a new repository
            if [[ -z "$2" ]]; then
                # Interactive mode: show remote repositories via gh + fzf
                local selected_repo
                selected_repo=$(gh repo list --limit 100 --json nameWithOwner --jq '.[].nameWithOwner' | fzf --height=50% --border --preview="gh repo view {} --json description,url,pushedAt --template '{{.description}}\n{{.url}}\nLast updated: {{.pushedAt}}'" --preview-window=down:5:wrap --prompt="Select repository to clone: ")
                if [[ -n "$selected_repo" ]]; then
                    ghq get "github.com/$selected_repo"
                fi
            else
                ghq get "$2"
            fi
            ;;
        "create"|"new"|"n")
            # Create a new repository directory
            if [[ -z "$2" ]]; then
                echo "Usage: repo create <repository_path>"
                echo "Example: repo create github.com/user/new-repo"
                return 1
            fi
            local repo_path="$(ghq root)/$2"
            mkdir -p "$repo_path"
            cd "$repo_path" || return 1
            git init
            echo "Created and initialized: $2"
            ;;
        "open"|"o")
            # Open repository in editor (default: code)
            local editor="${2:-code}"
            local selected_repo
            selected_repo=$(ghq list | fzf --height=50% --border --preview="echo {}" --preview-window=down:3:wrap)
            if [[ -n "$selected_repo" ]]; then
                local repo_path="$(ghq root)/$selected_repo"
                if command -v "$editor" > /dev/null; then
                    "$editor" "$repo_path"
                else
                    echo "Editor '$editor' not found. Trying fallback editors..."
                    if command -v code > /dev/null; then
                        code "$repo_path"
                    elif command -v vim > /dev/null; then
                        vim "$repo_path"
                    else
                        echo "No suitable editor found (code, vim)"
                    fi
                fi
            fi
            ;;
        "help"|"h"|*)
            # Show help
            cat << 'EOF'
Repository management with ghq and fzf

Usage: repo <command> [args]

Commands:
  list, l         List repositories with fzf selection
  cd, c           Change directory to selected repository
  remove, rm, r   Remove selected repository (with confirmation)
  get, g [url]    Clone repository (interactive if no url)
  create, new, n  Create and initialize a new repository
  open, o [editor] Open repository in editor (default: code)
  help, h         Show this help message

Examples:
  repo                    # List repositories
  repo cd                 # Change to selected repository
  repo get                # Interactive repository selection
  repo get github.com/user/repo
  repo create github.com/user/new-repo
  repo remove             # Remove selected repository
  repo open               # Open repository in code (default)
  repo open vim           # Open repository in vim
  repo open nvim          # Open repository in neovim

Requirements: ghq, fzf, git, gh (for interactive get)
EOF
            ;;
    esac
}

まとめ

gh、ghq、fzfを組み合わせることで、以下のメリットが得られます:

  • 統一された管理: ghqによるリポジトリの一元管理
  • 高速な検索: fzfによるインタラクティブな選択
  • 効率的な操作: GitHub APIとの連携による豊富な機能
  • 安全な実行: 危険な操作も選択ベースで実行可能

このワークフローを活用することで、複数のリポジトリを効率的に管理し、開発生産性を向上させることができます。

参考情報

Discussion