2019-12-06 [長年日記]

zshを導入した

業務の切り替わりで時間ができたので、ブートドライブをHDDからSSDに切り替えたところPCの応答がかなり良くなったので、zshを入れることにした。

色々試行錯誤したのだけど、最終的にzplugをベースにpreztoとその他便利そうなユーティリティーを入れることに。

  • prezto
  • zsh-completions
  • zsh-autosuggestions
  • zsh-syntax-highlighting
  • fzf

テーマはせっかくだしちょっと派手めにということで、Powerlevel10kにしてアイコンがもりもり表示されるようにした。

軽く使ってみた感じ、zsh-autosuggestionsとfzfがかなり便利で、VSCodeのgitプラグインが微妙に使いにくい感じのところが(VSCode内の)terminalを立ち上げてCUIで操作するのがよさそうな感触がある。

fzfに関しては、gitについていろいろなサンプルがあるのだけど、いまいちフィットする感じではなかったので結局ほぼフルスクラッチで書いてしまった。

function check-git-repository {
  git remote > /dev/null
  return $?
}

function fzf-git-add {
  check-git-repository || return $?

  local selected=$(git status -s -uall | awk '{if (substr($0,2,1) !~ / /) print $0}' |
    fzf -m --reverse --preview-window up:20 --exit-0 \
      --preview="[[ {} == '??'* ]] && echo {} | awk '{print \$2 }' | xargs colordiff -u /dev/null || 
    awk '{print $2}')
  if [[ -n "$selected" ]]; then
    selected=$(tr "\n" " " <<< $selected)
    git add $(echo $selected)
    echo "Completed"
  fi
}

function fzf-git-checkout {
  check-git-repository || return $?

  local branches=$(git branch -a)
  local branch=$(echo $branches | fzf --exit-0 --reverse \
    --preview-window up:20 \
    --preview="echo {} | awk '{print substr(\$0, 3)}' | xargs git log --color --graph --decorate=full
  [[ $branch == "" ]] && return
  branch=$(echo $branch | awk '{print substr($0,3)}')
  if [[ $branch == "remotes/"* ]];then
    # remote branch
    branch=$(echo $branch | sed "s#remotes/[^/]*/##")
    echo $branches | grep "^$branch$" > /dev/null
    if [[ $? == 0 ]]; then
      echo "$branch is already exist."
    fi
  fi
  git checkout $branch
}

git addはstage済みのファイルがリストアップされないようにとか、untracked fileは/dev/nullとdiffを取ることによってプレビューをそれっぽくするというのがポイント。

git checkoutはリモートブランチをチェックアウトしたときにエラーになったりdetachedにならないようにというのがポイントだけど、複数リモートリポジトリが存在する場合にどこまで正しく動作するかはチェックしていない。

実際はaliasに設定して3文字のコマンドで実行できるようにしてある。あとはdotfilesでの管理に置き換えたい。

お名前 : コメント :


2019-12-09 [長年日記]

ぼくのかんがえたさいきょうのfzf-git-checkout

先週適当に書いたスクリプトからいろいろ調べて、git branchが案外融通きかないな?というところからgit for-each-refを使えばいいんじゃないだろうかということで、サクッと作ってみた。

方針としては以下のような感じ。

  • 選択肢の表示(色)はなるべくgit branchに合わせる
    • HEADに*を表示し、リモートブランチは赤で表示
  • ローカルブランチはupstreamと状態を表示したい
  • リモートブランチを選択したときに、エラーになったりデタッチになってほしくない
function fzf-git-checkout {
  check-git-repository || return $?

  local branches=$(unbuffer git for-each-ref \
    --format='%(HEAD) %(if)%(HEAD)%(then)%(color:green)%(refname:short)%(color:reset)%(else)%(refname:short)%(end)%(if) %(upstream:short) %(then) -> %(color:red)%(upstream:short)%(color:reset) %(color:cyan)%(upstream:track)%(color:reset) %(end)' \
    refs/heads/)
  branches+="\n"
  branches+=$(unbuffer git for-each-ref \
    --format='  %(color:red)%(refname:short)%(color:reset)' \
    refs/remotes/)
  local branch=$(echo $branches | fzf --exit-0 --reverse --ansi \
    --preview-window up:`expr $LINES / 2` \
    --preview="echo {} | awk '{print substr(\$0, 3)}' | awk '{print \$1}' | xargs git log --color --graph --decorate=full -20 --date=short --format=\"%C(yellow)%h%C(reset) %C(magenta)[%ad] %C(cyan)%an%C(reset)%C(reset)%C(auto)%d%C(reset)%n       %s\"")
  [[ $branch == "" ]] && return
  branch=$(echo $branch | awk '{print substr($0,3)}' | awk '{print $1}')
  local refs=$(git show-ref $branch | awk '{print $2}')
  if [[ $refs == "refs/remotes/"* ]];then
    # remote branch
    b=$(echo $branch | sed "s#[^/]*/##")
    git checkout -b $b $branch 2> /dev/null || echo "$b is already exist"; git checkout $b
  else
    git checkout $branch
  fi
}

選択されたブランチに対して、git show-refによりrefspecを参照することによりリモートブランチかどうかを判定し、リモートのブランチの場合は-bでブランチを作成してdetachを防止する。これだけだとすでに同名のローカルブランチが存在した場合エラーになるので、エラーメッセージを表示しつつ、既存のローカルブランチをチェックアウトする。

この場合、リモートブランチとローカルブランチが別のものを指している場合等では意図するものとは別のものをチェックアウトしてしまう可能性があるけど、レアケースなのでメッセージを出すことにより許容範囲内ということにした。

branch_a -> origin/branch_a
origin/branch_a
upstream/branch_a
# のときに、upstream/branch_aをチェックアウトしようとすると、
# 意図せずorigin/branch_aをトラッキングしているbranch_aをチェックアウトしてしまう。
Tags: zsh fzf

お名前 : コメント :