輪ごむの空き箱

gitツールとしてのNeovim


この記事はVim駅伝2024年4月26日(金)の記事です。

前回の記事は ryoppippi さんの「Neovim + oil.nvim + Weztermで頑張って画像を表示する」という記事でした。

次回の記事は 4月29日(月) に投稿される予定です。


はじめに

Neovimは習熟するまでのハードルが高いと言われているエディタのひとつです。そのため、使いはじめたものの挫折してしまったという方も多いのではないでしょうか。
ですから、今回はテキストエディタとしてではなく、gitツールとしてNeovimを使う方法を紹介しようと思います。

git操作をNeovimでできるようになることで、あわよくばNeovim使いになってほしいなと願っています。

想定する読者

注意

windowsユーザーの皆さんすみません!!!windowsでは起動速度が遅いのでWSLで使うことを推奨します。
最終的なコードはこちらのレポジトリで公開しています。

GitHub - staticWagomU/neovim-as-a-git-tool: gitツールとしてのNeovim gitツールとしてのNeovim. Contribute to staticWagomU/neovim-as-a-git-tool development by creating an account...
ogp

実際の操作

必要なもの

Neovim

これがないと始まりません。
インストール方法はこちらを参照してください。

neovim/INSTALL.md at master · neovim/neovim Vim-fork focused on extensibility and usability. Contribute to neovim/neovim development by creating...
ogp

deno

deno製のプラグインを導入するため必要。インストール方法はこちらを参照してください。

必要なプラグイン

denops

denoでプラグインを動かすために必要な前提プラグイン

gin.vim

denopsを使って作られたgit操作をするためのプラグイン

gitsigns

これはdenops製ではない。

Vimの操作方法

今回使うのはNeovimですが、基本操作はvimと殆ど変わりません。
操作方法については、Vim駅伝2024年4月17日にyasunori0418さんが投稿した記事をご覧ください。

はじめのコード

まずはこのコードを貼り付けてください。

操作がわからない方はnvimを開いて"+yの順にキーを押すことでコピーしたものが貼り付けられます。あとは:wq<Cr>(<Cr>はエンターキー)の順に押すことで保存して終了することができます。

-- windowsユーザーの方は
-- %LOCALAPPDATA%\nvim\init.lua

-- mac/linuxユーザーの方は
-- ~/.config/nvim.init.lua

-- plugin managerの導入
local path_package = vim.fn.stdpath('data') .. '/site/'
local mini_path = path_package .. 'pack/deps/start/mini.deps'
if not vim.loop.fs_stat(mini_path) then
  vim.cmd('echo "Installing `mini.deps`" | redraw')
  local clone_cmd = { 'git', 'clone', '--filter=blob:none', 'https://github.com/echasnovski/mini.deps', mini_path }
  vim.fn.system(clone_cmd)
  vim.cmd('packadd mini.deps | helptags ALL')
  vim.cmd('echo "Installed `mini.deps`" | redraw')
end

vim.opt.number = true

require('mini.deps').setup { path = { package = path_package } }

local add, later = MiniDeps.add, MiniDeps.later
local opts = { noremap = true }

later(function()
  add('lewis6991/gitsigns.nvim')
  require('gitsigns').setup({
    signcolumn = false,
    numhl      = true,
  })
end)

later(function()
  add {
    source = 'lambdalisue/gin.vim',
    depends = { 'vim-denops/denops.vim' },
  }
  -- nvim起動時にGinStatusを実行する
  vim.api.nvim_create_autocmd('User', {
    pattern = "DenopsPluginPost:gin",
    callback = function()
      vim.schedule(function()
        vim.fn.execute('GinStatus')
      end)
    end,
    once = true
  })
end)


vim.cmd.colorscheme('habamax')

再度nvimを開くとプラグインのインストールが始まり、このような画面になればプラグインのインストールが完了です。
Pasted image 20240425172830.png
一度:q<Cr>でNeovimを終了してgitで管理しているディレクトリからnvimコマンドでNeovimを起動します。git status -sbの実行結果のような画面になれば成功です。
Pasted image 20240425172830.png
git管理していないディレクトリでNeovimを起動すると画面下部にエラーが表示されます。
Pasted image 20240425172830.png

各プラグインの役割

必要なプラグインで導入するプラグインの軽い説明をしましたが、もう少し具体的に説明します。

gin.vim

git操作全般を担当するプラグイン。
ファイルのステージ/アンステージ等の基本的な操作ははもちろんのこと、:Gin hogeと実行することでgit hogeを実行できるので、ターミナルに戻ることなく任意のgitコマンド実行ができる優れもの。

gitsigns.nvim

主にhunk単位での操作を行うために使います。
具体的にはhunk単位でのステージ/アンステージや、blameの確認、hunkの差分の確認ができます。

基本操作編

gin.vim

:Ginと入力した状態で<C-d>(Ctrlキーを押しながらdキーを押す)を押すとGinから始まるコマンド一覧を確認できます。
Pasted image 20240425172830.png
私がよく使う機能としては

です。逆にそれ以外のコマンドはほとんど使ったことがありません。ごめんなさい。

試しに:GinLog<Cr>を実行します。
このようにターミナルの実行結果がそのままNeovim内で見られます。いいですね。
commitのハッシュが書いてある行で<Cr>を押すとそのcommitの詳細を確認することができます。
Pasted image 20240425172830.png
この画面でaと入力すると画面下部にaction:と表示されます。
Pasted image 20240425172830.png
この状態で<C-d>を押すとGinLog内で実行できる様々なアクションを確認することができます。
Pasted image 20240425172830.png
このようにGinLogでは様々なアクションを実行することができます。
おすすめのアクションはfixup:instant-fixupです。詳細についてはこちらの記事に委ねます。

gin.vimで捗るgitのログ改竄 (instant fixup) Vim 駅伝の2024/3/15の記事です。 Gitで整然とコミットを詰むのはそうそうたやすいものではありません。 あのコミットでバグを仕込んでしまっ
ogp

aを押してアクションを選択することはGinStatusGinBranchでもできるので確認してみてください。

基本操作編にもかかわらず、ファイルのステージングの方法を書いていませんでした。
:GinStatus<Cr>を実行するとこのような画面になります。
Pasted image 20240425172830.png
ステージングしたいファイルの行で<<を押すことでステージングすることができます。
アンステージングは>>で行うことができます。

初期設定されているキーマッピングについては?を押すことで確認することができます。

gin.vimのヘルプを見たい方は、:h gin.txt<Cr>で確認してみてください。

gitsigns.nvim

gitsignsで定義されているコマンドを確認してみましょう
:Gitsigns (最後にスペース)と入力して<C-d>を押すと定義されているコマンド一覧を確認することができます。
Pasted image 20240425172830.png

:GinStatusでファイルの行へ移動して<Cr> をするとそのファイルを開くことができます。
そして:Gitsigns next_hunk<Cr>とすることで次のhunkへ移動することができます。これは言葉では説明が難しいので動画を撮ってみました。

動画を見ると分かるように、変更がされた行の行番号の色が変わっています。これもgitsignsの機能です。どの行が変更されたのか分かりやすくて気に入っています。

gitsigns.nvimのヘルプを見たい方は、:h gitsigns.txt<Cr>で確認してみてください。

カスタマイズ編

gin.vim

pushやpullをするときに:Gin push:Gin pullを毎回実行するのは面倒なので、キーマッピングを定義してみましょう。git pullには--autostashをつけたいタイプなので--autostash付きです。

later(function()
  add {
    source = 'lambdalisue/gin.vim',
    depends = { 'vim-denops/denops.vim' },
  }
  vim.api.nvim_create_autocmd('User', {
    pattern = "DenopsPluginPost:gin",
    callback = function()
      vim.schedule(function()
        vim.fn.execute('GinStatus')
      end)
    end,
    once = true
  })
+ vim.keymap.set('n', '<C-g>p', '<Cmd>Gin push<Cr>', opts)
+ vim.keymap.set('n', '<C-g>P', '<Cmd>Gin pull --autostash<Cr>', opts)
end)

これによって<C-g>pを押すことによってgit pushと同等の操作ができ、<C-g>Pを押すことによってgit pull --autostashと同等の操作ができるようになります。

:GinLog:GinBranch:GinStatusも毎回入力するのは面倒なので同様に定義してみましょう。

later(function()
  add {
    source = 'lambdalisue/gin.vim',
...

  vim.keymap.set('n', '<C-g>p', '<Cmd>Gin push<Cr>', opts)
  vim.keymap.set('n', '<C-g>P', '<Cmd>Gin pull --autostash<Cr>', opts)
+ vim.keymap.set('n', '<C-g><C-s>', '<Cmd>GinStatus<Cr>', opts)
+ vim.keymap.set('n', '<C-g><C-b>', '<Cmd>GinBranch<Cr>', opts)
+ vim.keymap.set('n', '<C-g><C-l>', '<Cmd>GinLog<Cr>', opts)
+ vim.keymap.set('n', '<C-g>c', '<Cmd>Gin commit<Cr>', opts)
end)

それ以外に私がよく使うコマンドとして、、

があるので一緒に定義していきましょう。

later(function()
  add {
    source = 'lambdalisue/gin.vim',
...

  vim.keymap.set('n', '<C-g>p', '<Cmd>Gin push<Cr>', opts)
  vim.keymap.set('n', '<C-g>P', '<Cmd>Gin pull --autostash<Cr>', opts)
  vim.keymap.set('n', '<C-g><C-s>', '<Cmd>GinStatus<Cr>', opts)
  vim.keymap.set('n', '<C-g><C-b>', '<Cmd>GinBranch<Cr>', opts)
  vim.keymap.set('n', '<C-g><C-l>', '<Cmd>GinLog<Cr>', opts)
+ vim.keymap.set('n', '<C-g>f', ':Gin fetch ', opts)
+ vim.keymap.set('n', '<C-g>m', ':Gin merge ', opts)
+ vim.keymap.set('n', '<C-g>r', ':Gin rebase --autostash ', opts)
end)

これでコマンドを打たずとも好きな操作ができるようになりました。

gitsigns.nvim

hunkを移動するために、:Gitsigns next_hunkを入力するのは面倒なのでこれもキーマッピングを定義します。

later(function()
  add('lewis6991/gitsigns.nvim')
  require('gitsigns').setup({
    signcolumn = false,
    numhl      = true,
  })
+ vim.keymap.set('n', ']g', '<Cmd>Gitsigns next_hunk<Cr>', opts)
+ vim.keymap.set('n', '[g', '<Cmd>Gitsigns prev_hunk<Cr>', opts)
end)

ついでに、自分の気に入っている設定も追加しておきます。

later(function()
  add('lewis6991/gitsigns.nvim')
  require('gitsigns').setup({
    signcolumn = false,
    numhl      = true,
  })
  vim.keymap.set('n', ']g', '<Cmd>Gitsigns next_hunk<Cr>', opts)
  vim.keymap.set('n', '[g', '<Cmd>Gitsigns prev_hunk<Cr>', opts)
+ vim.keymap.set('n', '<C-g><C-p>', '<Cmd>Gitsigns preview_hunk<Cr>', opts)
+ vim.keymap.set('n', '<C-g><C-v>', '<Cmd>Gitsigns blame_line<Cr>', opts)
+ vim.keymap.set('n', '<C-g>a', '<Cmd>Gitsigns stage_buffer<Cr>', opts)
end)

このようにカスタマイズを重ねていくと様々な操作ができるようになります。ぜひ自分だけのNeovimを作ってみてください。
私が実際に使っている設定も載せておきます。
https://github.com/staticWagomU/dotvim/blob/89e71e06d7fab936f52e4560d6aaae3e90ce3fec/nvim/init.lua#L335-L444

おわりに

今回はgitツールとしてNeovimを使う話です。
キーマッピングは自由なので好きにカスタマイズしてください!各プラグインのヘルプを読みながら設定するとより理解が深まるはずです。
これを機にNeovimに興味を持ってもらえると嬉しいです。

おまけ

Vim駅伝にてgin.vimについて取り上げた記事があります。興味がある方は読んでみてください。

gin.vimでgitの差分を快適に閲覧する 2023/11/29のVim駅伝記事です。 前回はyasunori0418さんによる「vimを切っ掛けにエンジニアになった話」でした。 gin.
ogp
git操作プラグインを探しているならgin.vimはどう? - 輪ごむの空き箱 git操作プラグインを探しているならgin.vimはどう?