3

Let's say I have a file buffer open and a :terminal split open in 1 tab page. I'd like to create a new tab where I run vimdiff but have my git diff showing. I'm trying to get git difftool HEAD~1 to show up in my current vim process, just in another tab page.

Is this possible on Vim 8 without plugins?

I kind of get this illusion when I do :tab terminal and then run git difftool HEAD~1. However, this starts a vim subprocess and I'd like to avoid that.

Kind of like this question: How do I enter into vimdiff mode given two splits are already open? but for git diffs.

425nesp
  • 576
  • 5
  • 16

1 Answers1

2

While this is certainly doable in vanilla vim, I suggest you take a look at tpope's excellent vim-fugitive plugin. Then, you simply need to do :tab Gdiff to get what you want.

If you really don't want to install any plug-ins, the following might work assuming the current file is in buffer no. 1:

  1. :tabnew | r! git show HEAD^:$(git rev-parse --show-prefix)#1:t
  2. In the newly opened tab, :vert sb 1 | windo diffthis

Explanation

  1. first opens a new tab with tabnew and loads the contents of the HEAD^ version of the file in buffer 1 into it.
  2. opens a vertical split containing buffer 1 with vert sb 1, then issues :diffthis to all buffers in the tab to enter diff mode.

Edit

The OP asked for some more explanation on step 1. git show needs an input of the form <rev>:<path> where <path> has to be relative to the root of the working tree. For example, if the absolute path of the file in buffer 1 is /a/b/foo.ext so that b contains your .git folder (i.e. b is the root of your repo), in order for git show to work properly you would have to invoke it with HEAD^:b/foo.e. Using either /a/b/foo.ext or foo.ext won't work. So I used git rev-parse --show-prefix to obtain the path of the current folder relative to the git root (which would be b/) in this example.

Then, I used vim path expansion to append the name of the file (that is foo.ext). #1 tells vim to fetch the path of whatever file is loaded in buffer 1, and :t extracts the "tail", which is everything after the last / in the path returned by #1. In some cases #1 and #1:t are equal, but this is not always the case. For instance, if you ran vim b/foo.ext then #1 would return b/foo.ext instead of just foo.ext. Check out :help expand for more info.

Of course, you could just enter the path manually, as in :tabnew | r! git show HEAD^:b/foo.ext, and it would work. But the version above is scriptable or you can assign to a map.

  • Cool! Could you explain step 1 a little more? I ran :tabnew | r! git show HEAD^:$(git rev-parse --show-prefix) and I get a list of files. But when I add #1 then I get the file contents. Is there a specific section in the manual that talks more about this syntax? (Also, what's :t at the end do?) – 425nesp Jan 20 '21 at 12:11
  • 1
    I think :help :% or :help :# will get you there – D. Ben Knoble Jan 20 '21 at 12:53
  • @425nesp see the updated answer – Nikolas Tapia Jan 20 '21 at 14:57
  • 3
    To avoid relying on the buffer-number: tabedit % | diffthis | vertical new | diffthis | read !git show HEAD^:#:t – D. Ben Knoble Jan 20 '21 at 16:26