藻ログ

都会でOLをしています

fugitive.vim と tig による git 生活

GWの出だしから熱を出して寝込んでいて,そういえば普段 fugitive.vim と tig をあんまりちゃんと使い分けてないなとぼんやり考えた.

fugitive.vim

GitHub - tpope/vim-fugitive: fugitive.vim: a Git wrapper so awesome, it should be illegal
vim から git を呼べるようにするラッパープラグイン
dein.vim で以下のように入れている.

[[plugins]]
repo = "tpope/vim-fugitive"
hook_add = 'source ~/.config/nvim/userautoload/plugins/plugins-vim-fugitive.vim'

dein については以下を参照.
dein.vimにお引越し - 藻ログ

使い方は :h fugitive を読めばだいたいわかる気がする.pushとかpullは手でやることが多いので頻繁に使うのは以下.

:Gstatus git status git status をサブウィンドウで開いていろいろできる
:Gcommit git commit 今開いてるファイルをコミット
:Gwrite git add 今開いてるファイルを git add
:Gdiff git diff 今開いてるファイルを前のコミットとvimdiff
:Gblame git blame 今開いてるファイルを git blame
:Gmerge git merge git merge [args] を呼ぶ.コンフリクト時に:Gmergeするとconflictをquickfixに流す

キーマップ

nnoremap [fugitive]  <Nop>
nmap <space>g [fugitive]
nnoremap <silent> [fugitive]s :Gstatus<CR><C-w>T
nnoremap <silent> [fugitive]a :Gwrite<CR>
nnoremap <silent> [fugitive]c :Gcommit-v<CR>
nnoremap <silent> [fugitive]b :Gblame<CR>
nnoremap <silent> [fugitive]d :Gdiff<CR>
nnoremap <silent> [fugitive]m :Gmerge<CR>

Space-gをfugitiveの専用キーのきもちで割り当てている. コミットは常にgit commit -vで行いたいので:Gcommitは-vをつけている.:Gmerge はコンフリクト時の呼び出しのみで使ってる.quickfixでコンフリクト箇所にジャンプできて良い.

:Gstatus

git status を実行してサブウィンドウで開いてくれる.自分の場合,サブウィンドウはせまいので新しいタブで開くように割り当てている*1.開かれた画面内では以下のマッピングで操作ができる. 選択ファイル上で- すれば git add/resetをtoggleして, D すれば git diff %{file}, Ugit checkoutという感じ.

g?    show this help
<C-N> next file
<C-P> previous file
<CR>  |:Gedit|
-     |:Git| add
-     |:Git| reset (staged files)
cA    |:Gcommit| --amend --reuse-message=HEAD
ca    |:Gcommit| --amend
cc    |:Gcommit|
cva   |:Gcommit| --amend --verbose
cvc   |:Gcommit| --verbose
D     |:Gdiff|
ds    |:Gsdiff|
dp    |:Git!| diff (p for patch; use :Gw to apply)
dp    |:Git| add --intent-to-add (untracked files)
dv    |:Gvdiff|
O     |:Gtabedit|
o     |:Gsplit|
p     |:Git| add --patch
p     |:Git| reset --patch (staged files)
q     close status
r     reload status
S     |:Gvsplit|
U     |:Git| checkout
U     |:Git| checkout HEAD (staged files)
U     |:Git| clean (untracked files)
U     |:Git| rm (unmerged files)

tig

端末で動作する有名なgitブラウザ.
GitHub - jonas/tig: Text-mode interface for git
Introduction · Tig - Text-mode interface for Git

vimから呼ぶ

fugitive.vim とできることが被ってる*2 のは承知なのだが,メインはfugitive.vimを使っていてちょっと複雑なことをやりたいときは tigの方で追ってる気がする.tigはneovim のターミナルで呼んでいる*3.

nnoremap tig :<C-u>w<CR>:te tig<CR>

manual

tig を立ち上げて h をタイプするとヘルプと現在設定されているキーバインドがまとめて表示されるので,ここを読むと大体なんとかなる.
移動とか検索はvimのような気持ちで触るとなんとなく動く.

view switching

tigには12種類ぐらいのviewがあり,viewごとにキーバインドや操作をカスタマイズできる.詳しくはmanualを参照されたい.
Manual · Tig - Text-mode interface for Git
左のキーで各viewの切り替えができる.

m main view
d diff view
b blame viewl
r refs view
s,S status view
y stash view

main view

tigを立ち上げると main view がデフォルトで表示される.main viewではコミットを選択すると,そのコミットの中身が見られる.
f:id:nisimur:20170503211430p:plain

status view

ここでは :Gstatus と大体同じことができる.
f:id:nisimur:20170503210631p:plain

ファイル選択 git diff
u git add/reset
C git commit

refs view

f:id:nisimur:20170503212024p:plain
ref-view からはcheckout やブランチ削除も普通にできる. *4

C git checkout %(branch)
! git branch -D %(branch)

ブランチ選択後のmain view

C git cherry-pick %(commit)

カスタマイズ

~/.tigrc にカスタムキーバインドとかを記述できる.
bind {view} {key} {action} のように指定する.たとえば以下のようなマクロ(もっとある)が使える. {view} に generic を指定すると全view共有でのバインディングになる.

%(head) The currently viewed head ID. Defaults to HEAD
%(commit) The currently selected commit ID.
%(branch) The currently selected branch name.
%(remote) The currently selected remote name. For remote branches %(branch) will contain the branch name.
%(file) The currently selected file.
%(lineno) The currently selected line number. Defaults to 0.

変な操作が走ると困るので,actionでは !(確認なし実行)と?(確認あり実行)を使い分けることができる.
例えばデフォルトのcherry-pick は

bind main C ?git cherry-pick %(commit)

となっていて,実行時にgit cherry-pick %(commit id) ? [Yy/Nn] みたいに聞いてくれるので安心.

Gを無効にする

vim の気持ちで最下行へ行きたくてGを押しまくってしまうが,デフォルトのキーバインドだと git gc が走ってしまうので無効化しておく

bind generic G none
commit の挙動を変える

常に git commit -v したすぎるのでデフォルトのCのバインドを上書きしておく.

bind status C !git commit -v 
ファイルcheckoutしたい

satus ビューで指定ファイルを直前のコミットに戻す.*5
間違って押すので ?モードで定義しておく.

bind status R ?git checkout %(file)
コミットIDにcheckoutしたい

これも間違って押すので?モードで定義しておく.

bind main c ?git checkout %(commit)
commit ID をクリップボードにコピる

チャットで送ったりするときに,commit IDが欲しかったりする.結局checkoutもcherry-pickも手でやりたかったりする.

bind generic 9 !@sh -c "echo %(commit) | pbcopy"

mac 以外は pbcopyをxclipとかxsel に読み替える.9はなんか良いのが思いつかなかった.

まとめ(でもない)

整理してるとリモートからブランチ取ってきたりとかマージしたりとかは割と手でやってて,今開いてるファイルに対してすぐなんかしたい(add/commit/diff)ときはfugitive.vim で,他は気分で使い分けてる気がしてきた.tigは大きいプロジェクトだとちょっとモッサリするのが気になるのでメインはfugitive.vimとシェルかも.

*1::h CTRL-W_T

*2:というかfugitive.vimかtigのどっちかで全部できる

*3:普段はvimから出ないので

*4:branch操作はシェルの補完で足りてるのでtig(重い)からはわざわざやってない

*5:自動で更新されてしまうファイルに対して頻繁にやりたいときがある