Vim 駅伝の2024/3/15の記事です。
Gitで整然とコミットを詰むのはそうそうたやすいものではありません。
あのコミットでバグを仕込んでしまった、コミットメッセージを間違えていた、そんなミスはよくあることです。
かと言って、整然とコミットするためにコミットを後回しにしては本末転倒です。
うかつな操作で作業内容を失うかもしれませんし、少し前の作業内容に戻りたくなるかもしれません。
また差分が大きくなるほど適切な粒度でのコミットが億劫になります。
そこで、VimでGitを操作するためのgin.vimというプラグインを使い、コミットの修正を簡単にするinstant fixupを実装しました。
magitのinstant fixupやlazygitのAmend an old commitに類する機能です。
デモ
以下の動画のように、特定のコミットに後から差分を追加したり、メッセージを変更したりできます。
便利なので使ってみてください。
instant fixupを使えると、とりあえずコミットを積んで後から改竄が容易です。
改竄しやすいコミットの積み方は意識した方がいいかもしれません。
本質的にはgit rebase
をしているのですが、大規模なrebaseはコンフリクトの元です。
instantにできることはさっさとやっておくと、コミットの並べ替えや合体といった複雑な操作を後から集注してできるのも魅力ですね。
解説
具体的な手順は以下のような感じ
- 修正したいコミットに混ぜたい変更をステージングしておく
- ステージング方法は任意。gin.vimを使う場合はファイル単位なら
:GinAdd
や:GinStatus
、パッチ単位なら:GinPatch {path}
が便利。
gitsigns.nvimを使えばバッファ上でカーソル位置のhunkやVisualモードで選択した範囲のステージングもできる。
:GinLog
を使ってログを表示する
git log
相当。:GinLog --graph --oneline -n 20
などとオプションの指定も可能
- 修正したいコミットにカーソルを合わせて
a
(<Plug>(gin-action-choice)
)を実行する - 修正方法に合わせて必要なinstant fixupを選択
fixup:instant-fixup
: ステージングした差分を選択したコミットに追加fixup:instant-reword
: コミットメッセージの書き換えfixup:instant-amend
: 上記2つを同時に実行- 今回の動画に示した例ならこれで一発
生のgitコマンドでやろうとすると、以下のようにそこそこ煩雑な操作です。
まずはgit commit --fixup
やgit rebase --interactive
を覚えるべし……という気持ちもありつつ、生ではinstant感を得られませんね。
git add
で修正対象のコミットに含める差分をstagingしておくgit log
で修正対象のコミットを特定するgit commit --fixup {commit}
で指定したコミットを修正するためのコミットを積むgit -c sequence.editor=true rebase --interactive --autostash --autosquash --quiet {commit}~
で修正対象のコミットに修正内容のコミットを混ぜつつ、以降のコミットを積み直す
Tips
マッピングでやりたい
Ginのアクションは実際には<Plug>(gin-action-fixup:instant-fixup)
などといったマッピングで定義されています。
頻繁に使うアクションは以下のようにバッファローカルなマッピングを定義すると便利かもしれません。
nnoremap <buffer> if <Plug>(gin-action-fixup:instant-fixup)
nnoremap <buffer> ir <Plug>(gin-action-fixup:instant-reword)
:GinLog
で表示しているコミットログはset nomodifiable
されているため、編集できません。
従ってi
でインサートモードに入ることもないので、if
やir
をinstant fixupやinstant rewordの略称として用いるのはそれなりに合理的だと思います。
Fuzzy Finderでやりたい
Ginのアクションがマッピングであることは先にも述べた通りです。従ってキーマップをfuzzy findして実行するようなプラグインを利用すると、目的のactionを探しやすくなります。
たとえば以下のようにすると、a
で本来はGinが実装しているアクション選択モードに入るところを、Telescopeによるアクション選択に差し替えられます。
vim.api.nvim_create_autocmd("BufReadCmd", {
group = vim.api.nvim_create_augroup("gin-custom", {}),
pattern = "ginlog://*" ,
callback = function(ctx)
vim.keymap.set("n", "a", function()
require("telescope.builtin").keymaps({ default_text = "gin-action " })
end, { buffer = ctx.buf })
end,
})
ENJOY!!!