CLIにおけるgitは奥が深い
以下のQiitaの記事で衝撃を受けました。
git操作はGUIツール派な自分もCUIに乗り換えた便利git拡張まとめ
めっちゃ便利!
とりあえずコピペで自分の環境に取り込んでみたものの、git力も無ければシェル力も無いためここで紹介されているコマンドで何が起きているのかサッパリわからん・・・
というわけで、紹介されているコマンドを題材にどのようなことが起きているのかを紐解いてみました。
git branch | fzf | xargs git checkout???
まずはgit branch
だけを実行してみる。
> git branch (git)-[CreateSchedule#2]
CreateFrontendEnv#7
* CreateSchedule#2
master
そりゃそうよね。
次はgit branch | fzf
を実行してみる。
これは、git branch
の内容をfzfに渡しているだけ。
>
3/3
> CreateFrontendEnv#7
* CreateSchedule#2
master
ブランチが選択できるようになったけど、この時点ではどれを選んでも何も起きない。(ブランチは切り替わらない。)
fzfについては、fzfを使おう等を参照。
ちなみに、私は普段は類似ツールのpecoを使ってますが、ここで紹介されているコマンドはfzfとそのオプションコマンドを使用しているので、pecoとは単純に置き換えできません。
最終的にxargs
で始まる部分を追加したらブランチが切り替わるようになるわけだけど、何が起きているのでしょうか?
xargs
コマンドに-pオプションをつければ、生成されるコマンドが見えるようになるらしい。
> git branch | fzf | xargs -p git checkout (git)-[CreateSchedule#2]
git checkout master ?...y
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
なるほど。fzfで選択したブランチがxargs
で指定されるコマンドに渡されて普通にgitのコマンドのようになって実行されているのがわかりました。
それでは、ここからが本番。
リモート含めたbranch一覧から一つ選んでcheckout
コマンドにすると以下のようになるようです。
co = "!f() { args=$@; if [ -z \"$args\" ]; then branch=$(git branch --all | grep -v HEAD | fzf --preview 'echo {} | cut -c 3- | xargs git log --color=always'); git checkout $(echo $branch | sed 's#remotes/[^/]*/##'); else git checkout $args; fi }; f"
うーん、わからん!!!
1行にしてつなげると構成を理解しづらいので改行を入れてみました。
!f() {
# ①
args=$@;
# ②
if [ -z \"$args\" ]; then
# ③
branch=$(git branch --all | grep -v HEAD | fzf --preview 'echo {} | cut -c 3- | xargs git log --color=always');
# ④
git checkout $(echo $branch | sed 's#remotes/[^/]*/##');
else
# ⑤
git checkout $args;
fi
};
# ⑥
f
①引数の格納
$@
はコマンドに渡された「全ての引数」を表す。これをargs
に格納して②で評価しています。
②引数の有無の確認
-zは「string の文字列長が 0 ならば真となる」。
ということで、当コマンドを実行する際に何らかのパラメーターを指定して実行していたら、②が真にならずに⑤の処理が実行されるようです。
③ブランチとそのコミットログを一覧化する
パラメーター未指定時はこちらが実行されます。
$(git branch --all | grep -v HEAD | fzf --preview 'echo {} | cut -c 3- | xargs git log --color=always')
分解してもまだまだ長い。
まずはgit branch --all
だけで実行してみる。--allを付けることで、リモートブランチも出力対象となります。
> git branch --all (git)-[CreateSchedule#2]
CreateFrontendEnv#7
* CreateSchedule#2
master
remotes/origin/CreateFrontendEnv#7
remotes/origin/CreateSchedule#2
remotes/origin/HEAD -> origin/master
remotes/origin/master
次はgit branch --all | grep -v HEAD
を実行してみる。
> git branch --all | grep -v HEAD (git)-[CreateSchedule#2]
CreateFrontendEnv#7
* CreateSchedule#2
master
remotes/origin/CreateFrontendEnv#7
remotes/origin/CreateSchedule#2
remotes/origin/master
-vオプションにHEAD
を指定することで、remotes/origin/HEAD
を抽出対象外としました。
いよいよgit branch --all | grep -v HEAD | fzf --preview 'echo {} | cut -c 3- | xargs git log --color=always'
を実行してみる。
これを実行することで、元記事のようにブランチのリストの右にコミットログが表示されるようになります。カーソルでブランチの選択を変えると、それに応じてログの内容も切り替わるのですが、何をしているのでしょうか?
- まず、
fzf --preview 'echo {}'
を指定することでfzfの結果をecho
コマンドでプレビューウインドウで表示します。 - 次に、プレビューウインドウで表示された内容を
cut -c 3-
します。 -cオプションは「切り出す位置のリストを文字数で指定する」というオプションで、3-は「3文字目から行末まで」を示すようです。ただ、私の環境ではcut -c 3-
は指定してもしなくても同じ結果になっているように見えます。。。 - 最後に、fzfで選択した内容を
xargs git log
に渡してログ内容をプレビュー表示している。color=always
を付けなければ一切のハイライトの無い状態で出力されますが、これを付けることでコミットログの内容がハイライトされるようになります。
④選択したブランチでcheckoutする
③で選択したブランチでgit branch
を実行します。
その際にsed 's#remotes/[^/]*/##'
で若干の整形をしています。remotes/origin/master
を選択した場合は、master
となるようになっています。
sed
の基本文法はsed "s/ABC/DEF/"
のような書き方が一般的ですが、区切り用の文字/
は#
などに変更することができます。というか、/
を使うのが一般的なだけで/
でないといけないわけではないです。
ディレクトリのように/
で区切られた文字列をsed
しようとするとエスケープが大変になりますが、その場合に#
を使用することでエスケープ地獄から脱出することができます。
⑤コマンド実行時にパラメーターを指定している場合の処理
パラメーターを指定した場合はこちらの処理が実行されます。
普通にgit checkout
が実行されるので、git co master
とすれば、git checkout master
が実行されます。あらかじめ切り替えたいブランチがわかっている場合でも同じコマンドを使用するための工夫ですね。
⑥実行
①〜⑤の内容は関数fとして定義されているので、関数fを実行しています。
元記事で紹介されている他のコマンドについて
元記事では他にも5つのコマンドが紹介されていますが、ぶっちゃけ面倒になってきたので 上記内容を理解できていれば他のコマンドのことも理解できそうなので以下省略です。
元記事にあるgitlabのリポジトリを覗いてみると記事では紹介されていなかったコマンドもあるようなので、それらも試してみたいと思います。
私自身も便利なコマンドを作れたらQiitaで情報展開したいと思います。