Vim: view git commit log without any plugins

07 Sep 2021

While many git integration plugins exists, all I simply wanted was a way to view git log interactively inside vim, nothing more.

The following is what I have ended up on, put it into your ~/.bashrc and simply run glo. Press K on top of a commit to view it, q to quit the buffer and Q to quit vim althogether.

glo() {
  git rev-parse 2> /dev/null || return 1
  local git_cmd="git --no-pager log --oneline --color=always ${@:--n 128}"
  vim \
    '+nnoremap q :bd!<CR>' '+nnoremap Q :qa!<CR>' \
    "+nnoremap <silent> K 0:tabnew \| setfiletype git \| exe 'read! git --no-pager show <C-r><C-w>' \| norm ggdd<CR>" \
    "+call term_start('$git_cmd', {'hidden': 1, 'term_cols': 2048, 'term_finish': 'open', 'term_opencmd': 'buffer %d'})"
}

If you're on neovim, change the entire last line from "+call term_start... to just "+term $git_cmd". Everything else remains the same.


Breaking it down

Let's start simple. Run git log | vim - and press K on top of a commit id. This will show you the commit diff in a terminal. There's a couple different problems with this:


Let's fix these

"+nnoremap <silent> K 0:tabnew \| setfiletype git \| exe 'read! git --no-pager show <C-r><C-w>' \| norm ggdd<CR>"

Here we are remapping K to:

Since we cannot use | after a read command, we're wrapping the read into an exe.

Now you can simply run

git log --oneline -n 128 | vim "+nnoremap <silent> K 0:tabnew \| setfiletype git \| exe 'read! git --no-pager show <C-r><C-w>' \| norm ggdd<CR>" -

and press K to open a commit. This will work on both vim and neovim.

This is already good enough and functional. But we don't have colors on our git log. Vim cannot parse ansi color escape sequence, so we cannot pipe the colored output into vim. However we can just open the git log inside a vim terminal buffer to get around this.

"+term ++curwin git --no-pager log --oneline --color=always"

On neovim remove ++curwin and you're basically done. But on vim, there's one more problem; the cursor will be on the bottom of the buffer instead of top. We can append --reverse to the git log command to fix this. But a proper solution is this:

"+call term_start('$git_cmd', {'hidden': 1, 'term_cols': 2048, 'term_finish': 'open', 'term_opencmd': 'buffer %d'})"

What this is doing is running our git command in a hidden terminal buffer, and once that's done, opening that buffer. This prevents the cursor from going down to the bottom.


Wrapping up

'+nnoremap q :bd!<CR>' '+nnoremap Q :qa!<CR>'

These are just some quality of life bindings. q to delete the buffer and Q to close vim entirely.

git rev-parse 2> /dev/null || return 1

On top of the shell function, this basically ensures that we're inside a git repo before doing anything. If we're not on a git repo then return early so we don't get a vim buffer with a git error message.

local git_cmd="git --no-pager log --oneline --color=always ${@:--n 128}"

This should be self explanatory. ${@:--n 128} at the end makes it so it defaults to showing only 128 commits if no arguments have been given to the function. Increase/decrease or remove this if you wish. But if you remove this, do be careful on running this command on large git repos with thousands of commits.

I think that takes care of everything.


Credits

Tags: [ cli ]



RSS Feed