Gitと連携するツールとしてのVim

## はじめに

Vimといえば、みなさんご存じテキストエディタですが、テキストエディタである以前に一つのCLIコマンドでもあります。そんなCLIコマンドの利点一つにThe UNIX philosophyの定理として提唱されている『7. Use shell scripts to increase leverage and portability. (シェルスクリプトによって梃子(てこ)の効果と移植性を高める)』というものがあります。

今回は私も普段の仕事で利用しているVimの梃子としての側面をご紹介できればと思います。

## GitコマンドとVim

今回Vimと組み合わせるのはシステム開発で避けては通れぬバージョン管理システムGitです。私は普段Gitを使用するときには[Sourcetree](https://www.sourcetreeapp.com/)や[GitKraken](https://www.gitkraken.com/)などのGUIクライアントを利用せずにCLIで操作をしています。
皆さんこう思われるかもしれません。「またCLI狂信者か。あんな簡素なインタフェースで、Gitなんて複雑なものを素早く操作できる訳無いだろう。おとなしくGUIクライアントを使えばいいじゃないか。」
ちょっとまってください。私もなにも素のGitコマンドだけですべてを操作することが効率が良いという気はないのです。CLIコマンドだからこそ簡単に他のツールと連携できることこそが魅力だと言いたいのです。

## コミットメッセージをVimで入力する

まずは基本のキ、コミットメッセージの入力です。Git管理ファイルを変更し、差分をaddしてstagedにします。そしたらさあコミットの時間です。
皆さんは普段GitのCLIコマンドでコミットするときにどうされるでしょうか？私はそのまま単純に`git commit`と入力します。オプションなしで実行するとそのままデフォルトのエディタ(環境変数$EDITORに設定されたコマンド)でGitのメッセージ入力用の一時ファイルを開き、ファイルを閉じたときにファイル入力内容を読み取ってコミットします。

![git-commit-plain.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/334ae726-9646-7090-0a07-d457ac3b02b8.gif)

補足すると、このとき最優先で開くエディタは`.gitconfig`ファイルに設定されているコマンドなので、明示的に指定したいときは以下のコマンドで指定すると良いでしょう。

```sh
git config --global core.editor ${editor}
```

ここからが面白いところです。素のVimを立ち上げただけだとそれなりに便利ではありますが少し物足りません。そこでいくつかの機能を追加してあげましょう。まずスペルチェックをGitのメッセージ編集時のみ有効にしてあげましょう。

```vim
augroup GitSpellCheck
    autocmd!
    autocmd FileType gitcommit setlocal spell
augroup END
```

スペルチェックだけだと、タイポはわかっても正しいスペルがわからないってときもありますよね。そんなときは辞書を引けばいいのです。[deoplete.nvim](https://github.com/Shougo/deoplete.nvim)と[neco-look](https://github.com/ujihisa/neco-look)を導入すれば、英単語が自動補完されるので、つまらないタイポとはオサラバです。

```vim
Plug 'Shougo/deoplete.nvim'
Plug 'ujihisa/neco-look'
let g:deoplete#enable_at_startup = 1
call deoplete#custom#option('sources', {
\ 'gitcommit': ['look'],
\})
```

![spell-check-and-completion.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/dda87d56-6865-61af-38ce-20f7a7ab7504.gif)

更に[committia.vim](https://github.com/rhysd/committia.vim)というプラグインを導入することで、変更したファイル内容などのコミットに付属する各種情報を一目で確認することができます。Vimでコミットメッセージをいじるなら是非入れたいプラグインです。

```vim
Plug 'rhysd/committia.vim'
function! g:committia_hooks.edit_open(info)
    " Additional settings
    setlocal spell
    setlocal spelllang+=cjk

    " If no commit message, start with insert mode
    if a:info.vcs ==# 'git' && getline(1) ==# ''
        startinsert
    end

    " Scroll the diff window from insert mode
    " Map <C-n> and <C-p>
    imap <buffer><C-n> <Plug>(committia-scroll-diff-down-half)
    imap <buffer><C-p> <Plug>(committia-scroll-diff-up-half)
endfunction
```

![Screen Capture_Alacritty_20201215215610.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/28d54abe-1dad-4cea-cf33-c5f3dd321ef8.png)

いかがでしょうか？Gitのコミット時に英単語のスペルチェックと自動補完と各種差分情報を見れてVimキーバインドでテキスト編集できる環境というのはなかなかないと思います。コミットメッセージを書くというVimが最も得意な行為をVimに任せる。それだけでGitコマンド単体と比べ格段に便利になるのです。

## Gitの差分をVimで見る

Gitで差分の確認もよくやる作業です。Gitコマンドで差分を見るなら以下のような流れですね。

```sh
# 差分を確認するハッシュをログから確認
git log
# 該当のハッシュの差分を見る
git diff ${hash}
```

![git-diff-plain.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/877a42df-6ca5-4d30-e34d-7157de60558a.gif)

上記を実行すると、だいたいの環境では`less`コマンドなどのページャが起動してスクロールすると思います。少量ならばこれでもいいですが、多量のdiffを確認するには心もとありません。そこで登場するのがVimです。皆さんVimのことをただのエディタだと思ってますよね？実はVimは差分比較ツールとしても優秀なのです。もし使ったことがないのならば一度でいいからdiffのかわりにvimdiffを使ってみて欲しいです。

Gitは先程のコミットメッセージのように、差分についても別コマンドに連携することができるのです。例えばWindowsで有名な差分ツールの[WinMerge](https://winmerge.org)でGitの差分を表示したり。なんてこともできます。

このような外部ツールをGitコマンドから起動するときは`diff`ではなく`difftool`を利用するのですが、このとき起動する外部ツールをeditorの変更をしたとき同様に設定で変更できます。

```sh
git config --global diff.tool vimdiff
```

設定を変更したらあとはコマンドを実行するだけです。


```sh
git difftool ${hash}
```

![git-difftool.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/a79c9750-3a0c-ddca-fa12-09979ca0f565.gif)

いかがでしょうか。とても見やすくなったのではないでしょうか？
差分以外の部分は折り畳まれてたりと、いい感じに気を利かせてくれていますね。

なお上記のGIF動画ではファイルごとに確認のプロンプトが表示されていますが、こちらについては設定で有無を選択できます。

```sh
git config --global difftool.prompt false
```

さて差分を確認できるとなると、少し欲が出てきますね。この見やすい差分の画面で、コンフリクト解消ができたらとっても便利なのに。そう思いませんか？

できます！！！！以下の記事に自分が以前参考にした内容が記載されているので見てみてください。

- [Gitのmergetoolでvimdiffを指定してVimでマージコンフリクトを解決してみた](https://yu8mada.com/2018/08/20/i-tried-resolving-a-merge-conflict-with-vim-by-specifying-vimdiff-at-git-s-mergetool/)

またvimdiffというのは本家Vim側が用意しているコマンドであって、NeoVimのコマンドではありません。そのため、NeoVimで同じことをやろうとするとまた別の設定が必要になります。こちらについては以下の参照ください。

- [GitのdifftoolとmergetoolコマンドがNeovimを使うように設定してみた](https://yu8mada.com/2018/08/21/i-tried-configuring-for-git-s-difftool-and-mergetool-commands-to-use-neovim/)

さてここまでくるとだいぶGitとVimの組み合わせのパワーを感じてきたのではないでしょうか？

## TigとVim組み合わせてインタラクティブにGit操作しよう

最後に組み合わせの要素をもう一つ追加します。それが[Tig](https://github.com/jonas/tig)です。俗に言うインタラクティブなGitコマンドです。
類似のコマンドにGolang製の[LazyGit](https://github.com/jesseduffield/lazygit)やRust製の[GitUI](https://github.com/extrawurst/gitui)なんかがあります。これらのほうがパフォーマンス面で優れていたりしているのですが、コマンドを形作る思想やカスタマイズ性が自分にマッチしすぎていて、tigを手放す気にはなりません。
tig単体でもかなり強力なツールなのですが、tigとVimを組み合わせることでもっと強力になります。

今回基本的な使い方については解説しないので[マニュアル](https://jonas.github.io/tig/doc/manual.html)をご参照ください。

### Tigでみた差分からVimを開く

私が多様するのがこれで、Tigの`Diff View`から`e`を押すことで、カーソル位置のファイルを`git editor`で開くというものです。シンプルが故にいくらでも応用が効きます。開いた後は普通にエディタとして操作が可能です。私はこれをプロジェクト編集の起点にしたりします。

![tig-diff-edit.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/ed1cc9fb-35e1-dccb-8fc4-2272732fd200.gif)

### Tigで差分を見てからrebaseしよう

皆さんチームメンバーに「このコミットとこのコミット粒度が小さすぎるから`squash`しておいて、`git rebase -i`でできるから。」と言われて、ナンノコッチャと調べて悪銭苦闘し、一時間ぐらいかけて２つのコミットを`squash`したことはありませんか？私はあります。
`git log`で該当のハッシュと差分を調べつつ、恐る恐る`git rebase -i`を実行する。そして次回やる頃にはどうやるかを忘れている。そしてrebaseなんかもう二度とやらないと思う。そんなことありませんか？私はあります。

そんな私はTigとたった一行の`.tigrc`(Tigの設定ファイル)でこの悩みから開放されました。それがこの`$HOME/.tigrc`です。

```.tigrc
bind main R !git rebase -i %(commit)
```

やってることは単純でTigの`main view`閲覧時の`R`でカーソル配下のcommitを起点に`rebase -i`を実行するだけです。

![tig-rebase-i.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/b3edfcea-f038-bda5-df61-4a8c38bf0dd8.gif)

なお豆知識なんですが、この`rebase -i`で起動したファイル上だと`commit hash`をカーソル下において`K`を押すとdiffが見れます。これはVimにビルドインされているプラグインの機能です。

### Tigの差分をVimで見る

自分で紹介しといてなんですが`Gitの差分をVimで見る`を更に便利にしたものをご紹介しましょう。`.tigrc`に以下を追加することで、その機能を利用できます。なお`git difftool`の設定ができていることを前提とします。

```.tigrc
bind main D !sh -c "git difftool %(commit)~ %(commit)"
```

これはTigでカーソル配下のcommitとその前のcommitの差分をvimdiffで表示できます。Gitコマンドでやっていたことをインタラクティブにできるようにしたと考えれば良いでしょう。

![tig-difftool.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24610/51e99e55-f082-0ba7-ef98-3e1dcf48c2c8.gif)

これは`main view`だけではなく`ref view`(ブランチの一覧)でも使えるため、ブランチの差分なんかを確認するのにも使えます。

```.tigrc
bind refs D !sh -c "git difftool %(branch)"
```

## 最後に

Vimというコマンドの中だけでも面白いですが、Vimとて一つのCLIコマンドです。その特色を活かして、他のコマンドからVimへ、Vimから他のコマンドへの組み合わせることで、相乗効果が生まれるということが少しでもわかっていただけたなら幸いです。
もし皆さんのオススメの組み合わせなどあれば、ぜひ私に教えてください。