Git の difftool と mergetool コマンドが Neovim を使うように設定してみた


Git の difftool と mergetool は -t オプションで vimdiff を指定すれば Vim の vimdiff を使ってくれますが, Vim ではなく Neovim を使ってくれるようにコマンドラインを設定してみました.

ファイルを見比べたり, マージコンフリクトを解決する時に git difftool -t vimdiffgit mergetool -t vimdiff とそれぞれ -t オプションで vidiff と指定すれば, Vim の vimdiff コマンドを使ってくれます.

ただ僕は, 今 Neovim を使っているということもあり, Neovim で vimdiff と同じことがしたいと思い, どうすればいいのか調べてみまして, 実際に git difftoolgit mergetool が Neovim を使ってくれるように設定することができましたので, 同じようにそれらのコマンドで Neovim を使ってみたいという方は, 一例として参考にしていただければと思います.

設定方法

次のコマンドで ~/.gitconfig を開いて:

git config --global --edit

次の変数を設定します:

[diff]
  tool = nvimdiff
[difftool "nvimdiff"]
  cmd = "nvim -R -d -c \"wincmd l\" -d \"$LOCAL\" \"$REMOTE\""
[mergetool "nvimdiff"]
  cmd = "nvim -d -c \"4wincmd w | wincmd J\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"  \"$MERGED\""

これで設定完了です.

あとは変更を保存すれば, 変更が適用されるので, git difftool, git mergetool コマンドで Neovim を起動してくれるようになります.

設定の説明

カスタム diff ツールの場合

diff.tool の値を nvimdiff にして, カスタム diff ツールとして設定しています.

カスタム diff ツールを設定すると, それに対応する difftool.nvimdiff.cmd の値を設定する必要があるので, シェルコマンドを設定しています.

シェルコマンドの内容は nvim でまず Neovim の実行ファイルを実行し, -R オプションで読み取り専用モードにし, -d オプションで diff モードにし, -c オプションでコマンドを実行するようにし, そのコマンド内容の wincmd l で Neovim で $LOCAL$REMOTE の 2 つのファイルを 2 つのウィンドウを縦に並べるように開いた後に, ウィンドウのフォーカスを右に 1 つ移動させます.

カスタムマージツールの場合

merge.tool の値を nvimdiff にして, カスタムマージツールとして設定しています.

カスタムマージツールを設定すると, それに対応する mergetool.nvimdiff.cmd の値を設定する必要があるので, シェルコマンドを設定してします.

シェルコマンドの内容は nvim でまず Neovim の実行ファイルを実行し, -d オプションで diff モードにし, -c オプションでコマンドを実行するようにし, そのコマンド内容の 4wincmd w | wincmd J で Neovim で $LOCAL, $BASE, $REMOTE, $MERGED という 4 つのファイルを 4 つのウィンドウを縦に並べるように開いた後に, まず 4 番目のウィンドウ ($MERGED) にフォーカスを移動させ, そのウィンドウを画面下半分に持ってくるように配置転換させます.

実際に使ってみる

git difftool の場合

一例として, ワーキングディレクトリの example.txt を変更した後に git difftool を実行した直後は次のようになります:

Git difftool

git mergetool の場合

一例として, master ブランチに hotfix ブランチをマージさせて, マージコンフリクトが発生した直後に git mergetool を実行した直後は次のようになります:

Git mergetool

まとめ

今回の設定を調べるにあたって, Git (v2.18.0) のリポジトリの mergetools/vimdiff ファイルの中身を拝見させていただいたところ:

https://github.com/git/git/blob/v2.18.0/mergetools/vimdiff

次のようなコードを確認できました:

# 1
"$merge_tool_path" -R -f -d \
  -c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"

# 2
"$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \
  "$LOCAL" "$BASE" "$REMOTE" "$MERGED"

この上記のコードを参考にさせていただいて, 今回の設定をすることができました.

ただ, 元のファイルを見ていただくとわかるのですが, merge.toolvimdiff の場合, そちらのコードはマージコンフリクトが発生した際に, ベースのファイルが存在しない場合, 3 つのウィンドウを開くような処理も実装されているのですが, 今回の merge.nvimdiff.cmd のコードはもしベースのファイルが存在しない場合でも 4 つのウィンドウを開いてしまいます.

ただ Git でマージする場合, git checkout --orphan <branch> で作ったブランチを git merge --allow-unrelated-histories <branch> でマージしない限り, 通常 3-way マージになるかと思いますので, そんなに大きな問題はないと思われます.

なので今回の設定でもそれなりに実用性があるかと思いますので, 一例として参考にしていただけたらなと思います.

ただ一番いいのは difftool.nvimdiff.cmdmergetool.nvimdiff.cmd など設定することなく, diff.toolmerge.tool の値を nvimdiff などと設定するだけで, Git が Neovim を diff モードで起動するように実装してくれれば, それに越したことはないのでしょう relaxed

参考資料