4

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.

1 Answer 1

3

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.

4
  • 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
    Commented Jan 20, 2021 at 12:11
  • 1
    I think :help :% or :help :# will get you there
    – D. Ben Knoble
    Commented Jan 20, 2021 at 12:53
  • @425nesp see the updated answer Commented Jan 20, 2021 at 14:57
  • 4
    To avoid relying on the buffer-number: tabedit % | diffthis | vertical new | diffthis | read !git show HEAD^:#:t
    – D. Ben Knoble
    Commented Jan 20, 2021 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.