---
title: 'gitツールとしてのNeovim'
pubDate: 2024-04-26T17:18:02+09:00
published: true
tags: ["vim駅伝"]
---

この記事は[Vim駅伝](https://vim-jp.org/ekiden/)2024年4月26日(金)の記事です。

前回の記事は [ryoppippi](https://github.com/ryoppippi) さんの「[Neovim + oil.nvim + Weztermで頑張って画像を表示する](https://zenn.dev/vim_jp/articles/5b5f704de07673)」という記事でした。

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

---

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


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

## 想定する読者
- `git commit`のときにしかvimを起動しない人
- VSCodeなどのguiからgitを使っている人
- tuiツールでgitを使っている人

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

https://github.com/staticWagomU/neovim-as-a-git-tool

## 実際の操作
![](/images/blog/2024-04-26-vim-ekiden/20240426-0805-44.gif)

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

https://github.com/neovim/neovim/blob/master/INSTALL.md

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

https://docs.deno.com/runtime/manual

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


### gin.vim
denopsを使って作られたgit操作をするためのプラグイン
### gitsigns
これはdenops製ではない。

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

https://zenn.dev/vim_jp/articles/0009-just_want_to_be_able_to_use_for_vim_begineer

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

操作がわからない方はnvimを開いて`"+y`の順にキーを押すことでコピーしたものが貼り付けられます。あとは`:wq<Cr>`(`<Cr>`はエンターキー)の順に押すことで保存して終了することができます。
```lua
-- 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](/images/blog/2024-04-26-vim-ekiden/image20240425172830.png)
一度`:q<Cr>`でNeovimを終了してgitで管理しているディレクトリからnvimコマンドでNeovimを起動します。`git status -sb`の実行結果のような画面になれば成功です。
![Pasted image 20240425172830.png](/images/blog/2024-04-26-vim-ekiden/image20240425172952.png)
git管理していないディレクトリでNeovimを起動すると画面下部にエラーが表示されます。
![Pasted image 20240425172830.png](/images/blog/2024-04-26-vim-ekiden/image20240425172916.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](/images/blog/2024-04-26-vim-ekiden/image20240425223451.png)
私がよく使う機能としては
- `git`コマンド相当の`Gin`
- `git log`相当の`GinLog`
- `git branch`相当の`GinBranch`
- `.git`ディレクトリがある階層に自動的に`cd`してくれる`GinCd`
- `git status`相当の`Gin`Status

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


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

https://blog.atusy.net/2024/03/15/instant-fixup-with-gin-vim/

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


基本操作編にもかかわらず、ファイルのステージングの方法を書いていませんでした。
`:GinStatus<Cr>`を実行するとこのような画面になります。
![Pasted image 20240425172830.png](/images/blog/2024-04-26-vim-ekiden/image20240425172952.png)
ステージングしたいファイルの行で`<<`を押すことでステージングすることができます。
アンステージングは`>>`で行うことができます。


初期設定されているキーマッピングについては`?`を押すことで確認することができます。
![](/images/blog/2024-04-26-vim-ekiden/20240426171529.png)


gin.vimのヘルプを見たい方は、`:h gin.txt<Cr>`で確認してみてください。
### gitsigns.nvim
gitsignsで定義されているコマンドを確認してみましょう
`:Gitsigns `(最後にスペース）と入力して`<C-d>`を押すと定義されているコマンド一覧を確認することができます。
![Pasted image 20240425172830.png](/images/blog/2024-04-26-vim-ekiden/image20240425225748.png)

`:GinStatus`でファイルの行へ移動して`<Cr>` をするとそのファイルを開くことができます。
そして`:Gitsigns next_hunk<Cr>`とすることで次のhunkへ移動することができます。これは言葉では説明が難しいので動画を撮ってみました。
![](/images/blog/2024-04-26-vim-ekiden/20240425-1410-14.gif)
動画を見ると分かるように、変更がされた行の行番号の色が変わっています。これもgitsignsの機能です。どの行が変更されたのか分かりやすくて気に入っています。

gitsigns.nvimのヘルプを見たい方は、`:h gitsigns.txt<Cr>`で確認してみてください。
## カスタマイズ編

### gin.vim

pushやpullをするときに`:Gin push`や`:Gin pull`を毎回実行するのは面倒なので、キーマッピングを定義してみましょう。`git pull`には`--autostash`をつけたいタイプなので`--autostash`付きです。
```diff
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`も毎回入力するのは面倒なので同様に定義してみましょう。
```diff
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)

```

それ以外に私がよく使うコマンドとして、、
- git fetch
- git merge
- git rebase

があるので一緒に定義していきましょう。
```diff
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`を入力するのは面倒なのでこれもキーマッピングを定義します。
```diff
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)
```

ついでに、自分の気に入っている設定も追加しておきます。
```diff
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について取り上げた記事があります。興味がある方は読んでみてください。

https://blog.atusy.net/2023/11/29/gin-diff/

https://wagomu.me/blog/2023-12-12-vim-adventcalendar/

