This tweet blew my mind.
$ git log | vim -R –
Now press <K> on a commit hash.— Luke Diamand (@LukeDiamand) February 21, 2018
I spend a lot of my time in Vim and Git and this is an amazing combination of the two. While I love this command, I think we can do even better (jump to my version).
What is going on?
$ git log | vim -R -
Running this command, executes git log
and opens a copy of Vim populated with the results of git log
. Once you’re in Vim, if you place your cursor over a commit (e.g. 083279917ae3bbe8bfc25b2bf785acbbee302415
) and click K
, you’ll be temporarily shown the result of running git show
on the commit.
Why does this work?
When you’re in Vim, the default behavior when you click K
is to take the word under the cursor and look it up using the man program. This behavior can be modified by setting the value of keywordprg to something other than man
.
Vim automatically sets keywordprg
for some filetypes, including the filetype git
. When the filetype is set to git
, keywordprg
is set to git show
.
How does Vim know it is Git?
This is a great question that I don’t know the answer to. I’m guessing Vim recognizes the standard format of git log
.
Can We Improve This?
I don’t love the standard format for git log
. I have an improved version of git log that I prefer to use. This made me wonder if we can get this same commit view behavior in Vim with a log format I prefer. This is what I came up with.
git log --graph --pretty=format:'%h - %d %s (%cr) <%an>' | vim -R -c 'set filetype=git nowrap' -
Explanation
Git Output
Unfortunately, I couldn’t use my improved version of git log as it appears in that article because it includes color values, which muddy up the output in Vim. Instead, I’m using a simplified version.
git log --graph --pretty=format:'%h - %d %s (%cr) <%an>'
Here are the relevant links if you want to read about how this works.
- –graph
- –pretty=format
%h
– abbreviated commit hash%d
– ref names, like the –decorate option of git-log%s
– subject%cr
– committer date, relative%an
– author name
Vim Settings
When I feed my customized git log
output into Vim, Vim fails to set the filetype to git
.
To correct this we include -c 'set filetype=git'
.
We also set nowrap so the lines do not wrap when they are displayed.
-R is used to start in read-only mode.
Notes about Neovim
If you’re using Neovim, you’ll find that when you view a commit you can only see the end of the commit and you can not scroll back up. Instead you see the message “Press ENTER or type command to continue”.
This is due to how Neovim handles bang/system commands, as the author of Neovim, Thiago de Arruda, writes in this comment on Neovim Issue 1496
This is not a bug, it is the new behavior of bang commands: We no longer spawn the program with it’s stdout connected to Nvim tty, instead we open a pipe, read output and display to the user. This is the only way the bang commands will be consistent across UIs, so programs designed to be used interactively from the terminal will no longer work from inside nvim.
If you’re interested in learning more about this, these two issues have some good information.
- :! (bang) and system() are not interactive / show ANSI codes
- Use :terminal for opening shell commands found in &keywordprg
The following works for me in Neovim
git log --graph --pretty=format:'%h - %d %s (%cr) <%an>' | nvim -R -c 'set hidden nowrap keywordprg=:enew\ \|\ terminal\ \git\ --no-pager\ show | nnoremap q :bd!<cr>' -
The Git portion of the mapping is the same but the Vim/Neovim part works differently.
- set
hidden
allows switching away from an unsaved buffer - set
nowrap
turns off line wrapping (so single line commits - set
keywordprg
:enew
opens a new buffer and switches to it (away from our unsaved buffer)terminal
starts a terminal sessiongit --no-pager show
runs git show in the terminal without pagingnnoremap q :bd!<cr>
remapsq
to quit the buffer
Next Steps
My next step is to setup this command as a Bash alias, so it is easier to run.
> I’m guessing Vim recognizes the standard format of git log
Yup. See:
Hi Rich,
Thanks for confirming and especially thanks for the command to see the relevant code – that is awesome.
If anyone else want to see the relevant code, running Rich’s line in Vim will take you to the correct spot in the correct file. You can also see the code on GitHub.
In
~/.gitconfig
(or wherever else you store git config)the pager variable can receive bash expressions, e.g:
pager = if [ $X = "this"] then vim -R ; else less
… Which is easier than creating aliases 🙂
Moreover, the AnsiEsc.vim plugin enables ANSI escape coloring in vim:
https://github.com/vim-scripts/AnsiEsc.vim
so Sal’s original improved-version-of-git-log can actually be made to work, with two caveats:
1. the `:AnsiEsc` command must be executed first; and
2. when pressing ‘K’ over a commit hash, vim thinks that the ANSI escape codes are part of the commit hash itself.
The following works:
git log –color –graph –pretty=format:’%Cred %h %Creset-%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)%Creset’ | vim -R -c ‘set filetype=git nowrap’ -c ‘AnsiEsc’ –
Combining your git config lines with his:
# ~/.config/git/config
[core]
pager = “vim -R -c ‘set filetype=git nowrap’ -c ‘AnsiEsc’ -”
[alias]
lg = log –color –graph –pretty=format:’%Cred %h %Creset-%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)%Creset’
this is very cool! and makes me think of a few ideas with keywordprg, but for git history viewing i still think the gv plugin is better (colors, diffs open in splits, more options, more interactive, etc) https://github.com/junegunn/gv.vim
I don’t think it is a good idea to pipe the git log output to vim (git log | vim -R -)
If the project has a huge log history then it takes forever to complete the operation.
You’re right, keeping the possibility of a very large number of commits in mind is a good idea. My first thought would be to limit the results to the first n commits, e.g.
--max-count=1000
. Thanks for bringing this up.