hotoolong's blog

プログラムのことやエンジニアリングに関することを記事にしています。

ghコマンドとfzfコマンドのbind、previewを使って開発フローを見直す

概要

よく利用するコマンドが洗練されている方がいいのですが、
時間をかけて修正する大変でなかなか重い腰が上がらないです。

基本的にGitHubを使っての開発が多いのですが、 コマンドライン(以下CLI)とブラウザの行き来が多いです。

pecoを使っていたのですが、 fzfのpreviewが便利そうだったので previewを使いつつghと連携して改善してみました。 調べているうちにfzfのbindも便利そうなので使ってみました。

フローの概要

改善前のフロー

既存のissue prを確認するフロー

  • (ブラウザ) PullRequest(以下PR)を確認
  • (ブラウザ) PRからブランチを確認
  • (CLI) ブランチをチェックアウト
  • (CLI) コード修正、動作確認
  • (CLI) git add/commit/push
  • (ブラウザ) PRを確認

新規に開発するフロー

  • (ブラウザ) issueの確認
  • (CLI) ブランチを作成
  • (CLI) コード修正、動作確認
  • (CLI) git add/commit/push
  • (ブラウザ) PRを作成

改善後のフロー

既存のissue prを確認する場合

  • (CLI) PullRequest(以下PR)を確認 (CLI)
  • (CLI) PRのブランチをチェックアウト(CLI)
  • (CLI) コード修正、動作確認
  • (CLI) git add/commit/push
  • (ブラウザ) PRを確認

新規に開発する場合

  • (CLI) issueの確認
  • (CLI) ブランチを作成
  • (CLI) コード修正、動作確認
  • (CLI) git add/commit/push
  • (ブラウザ) PRを作成

改善した点

ghがissue, PRの一覧確認ができるので
fzf と組み合わせてブラウザの確認をCLIに変更します。
最後のPR確認はブラウザで画像をアップすることもあるのでCLIへの移行は難しそうです。

使用したコマンドのバージョン

command version
gh 0.6.4
fish 3.1.0
git 2.26.0
fzf 0.21.0

コマンド別の修正内容

改善のためにfish shellのfunctionを作成しています。 作成したfunctionは以下の3通りです。

  • issue確認のfunction
  • PR確認のfunction
  • git statusのfunction

issue確認のfunction

実際にfunctionを見ていきましょう。

function fzf_git_issue
  set -l query (commandline --current-buffer)
  if test -n $query
    set fzf_query --query "$query"
  end

  set -l base_command gh issue list --limit 100
  set -l bind_commands "ctrl-a:reload($base_command --state all)"
  set bind_commands $bind_commands "ctrl-o:reload($base_command --state open)"
  set bind_commands $bind_commands "ctrl-c:reload($base_command --state closed)"
  set -l bind_str (string join ',' $bind_commands)

  set -l out ( \
    command $base_command | \
    fzf $fzf_query \
        --prompt='Open issue list >' \
        --preview "gh issue view {1}" \
        --bind $bind_str \
        --header='C-a: all, C-o: open, C-c: closed' \
  )
  if test -z $out
    return
  end
  set -l issue_id (echo $out | awk '{ print $1 }')
  commandline "gh issue view -w $issue_id"
  commandline -f execute
end

gh を使ってopenなissueリストを一覧化してfzfに食わせています。
fzfpreviewgh issue view の内容を表示しています。
基本的にはCLIから確認し詳細を確認して、Enterでブラウザが開きます。

デフォルトではopenのissueだけを確認できるようにしておき、
issueのステータスがclosedを確認したい場合はCtrl-cで一覧をリフレッシュしています。
同様にすべてのステータスを確認したい場合はCtrl-aでリフレッシュしています。

選択したissueでEnterするとブラウザで確認できるようにしています。

PRの確認function

PRの確認も基本的にissueと同じように作っています。

function fzf_git_pull_request
  set -l query (commandline --current-buffer)
  if test -n $query
    set fzf_query --query "$query"
  end

  set -l base_command gh pr list --limit 100
  set -l bind_commands "ctrl-a:reload($base_command --state all)"
  set bind_commands $bind_commands "ctrl-o:reload($base_command --state open)"
  set bind_commands $bind_commands "ctrl-c:reload($base_command --state closed)"
  set bind_commands $bind_commands "ctrl-g:reload($base_command --state merged)"
  set bind_commands $bind_commands "ctrl-a:reload($base_command --state all)"
  set -l bind_str (string join ',' $bind_commands)

  set -l out ( \
    command $base_command | \
    fzf $fzf_query \
        --prompt='Select Pull Request>' \
        --preview="gh pr view {1}" \
        --expect=ctrl-k,ctrl-m \
        --header='enter: open in browser, C-k: checkout, C-a: all, C-o: open, C-c: closed, C-g: merged, C-a: all' \
  )
  if test -z $out
    return
  end
  set -l pr_id (echo $out[2] | awk '{ print $1 }')
  if test $out[1] = 'ctrl-k'
    commandline "gh pr checkout $pr_id"
    commandline -f execute
  else if test $out[1] = 'ctrl-m'
    commandline "gh pr view --web $pr_id"
    commandline -f execute
  end
end

ghをつかってPRの一覧を取得しています。
こちらもデフォルトだとステータスがopenのものに絞られている為、
マージ済みものも確認する場合はCtrl-gで表示し直していたりします。
PRの場合は特にマージ済みの情報を見たくなったりしているので初期コマンドをallで確認してもいいかもしれないです。
件数は使っているうちに変更したくなるやもしれませんが
デフォルトだと30件なので一旦は100件にしています。

ここで
Ctrl-kgh pr checkout <PRのid> を使ってcheckoutできます。
このコマンドは便利ですね。gh 様々です。
ブラウザでPRを確認してbranch名をコピーしてブランチの切り替えをやる作業をスムーズにすることができます。

git statusのfunction

git statusgstとしてaliasに設定していたのですが、
fzfのpreview、bindを組み合わせて改善しています。

function gst --description 'git status -s'
  if ! is_git_dir
    return
  end
  set -l base_command git status -s
  set -l bind_reload "reload($base_command)"
  set -l bind_commands "ctrl-a:execute-silent(git add {2})+$bind_reload"
  set bind_commands $bind_commands "ctrl-u:execute-silent(git restore --staged {2})+$bind_reload"
  set -l bind_str (string join ',' $bind_commands)

  set -l out (command $base_command | \
    fzf --preview="git diff {2}" \
        --expect=ctrl-m,ctrl-r,ctrl-v,ctrl-c \
        --bind $bind_str \
        --header='C-a: add, C-u: unstage, C-c: commit, C-m(Enter): mv, C-r: rm, C-v: edit' \
  )
  [ $status != 0 ]; and commandline -f repaint; and return

  if string length -q -- $out
    set -l key $out[1]
    set -l file (echo $out[2] | awk -F ' ' '{ print $NF }')

    if test $key = 'ctrl-m'
      commandline -f repaint
      commandline "git mv $file "
    else if test $key = 'ctrl-r'
      commandline "git rm $file "
      commandline -f execute
    else if test $key = 'ctrl-v'
      commandline "$EDITOR $file"
      commandline -f execute
    else if test $key = 'ctrl-c'
      commandline "git commit -v"
      commandline -f execute
    else
      commandline -f repaint
    end
  end
end

git status -s の結果を fzf に食わせています。

preview には git diff <選択ファイル> で差分内容が表示されます。

fzfのbindでいくつかのコマンドを設定しています。
Ctrl-agit add <選択ファイル> して fzf をリフレッシュしています。
Ctrl-ugit restore --staged <選択ファイル> して fzf をリフレッシュしています。
Ctrl-cgit commit -v します。
この3つの操作で基本的にcommitまで持っていくようにしています。

あとgit statusの内容を見ていてdiffなどから修正したいことがあったので、
Ctrl-v で エディタでのファイル編集を呼び出しています。私の場合はnvimになります。

付属としてgit rm/mvを用意していますが、おらくgit statusする前にコマンド打ちたくなりそうかな思われます。

まとめ

fzfのpreviewは他の人のブログを確認すると有効に利用している人を多々見かけます。 すべてをCLIで収めるのは難しかと思いますが、極力CLIで済ませることができそうです。

特にbindはブログに記載している人を見かけてなかったので これを見て利用してくれる方が出てくれば嬉しい限りです。