Question

The command :Gdiff is equivalent to running git diff on that file.

What's the equivalent for git diff --staged or git diff --cached?

Was it helpful?

Solution

I've found a way to do this. Run :Gstatus, you should get a window with contents like the following:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   example.txt
#

Scroll down to the staged file, example.txt, and press Shift+D. This will open a diff view, comparing what's in HEAD and what's in the index. You'll notice on the bar on the bottom that both the filenames are special Fugitive filenames.

Also while in Gstatus preview window, you can press g?, which will list all the mappings valid in the current context.

OTHER TIPS

While vim-fugitive does not supply direct analogues for git diff --staged or git diff --cached, it does supply a general-purpose Vim command for piping the output of arbitrary git commands into read-only Vim buffers: :Git!.

Inapplicable Answers

Before we get to that, let's explicitly restate the question. git diff --staged and git diff --cached are synonyms for the same underlying operation: diffing the contents of the index (the set of all staged changes) against the contents of the HEAD (the most recent commit for the current branch), typically for reviewing changes prior to commit. The stated question then becomes:

What is the most effective means of reviewing all staged changes in vim-fugitive?

It should be clear that the currently accepted self-answer fails to address this question. The next highest rated self-answer is no better.

:Gstatus bindings only apply to the file on the current line and hence cannot by definition be used to review all staged changes. Moreover, the :Gstatus D binding doesn't even review all staged changes for the file on the current line. It only diffs the index and working tree copies of that file, rather than diffing the index and most recently committed copies of that file (which is an altogether different beast).

:Gdiff HEAD is similarly inapplicable. It only diffs the most recently committed and working tree copies of the file corresponding to the current buffer. :Gdiff without an argument is equivalent to the :Gstatus D binding, again diffing the index and working tree copies of that file. Neither reviews all staged changes.

Applicable Answers

emaniacs struck the closest to a working solution with this comment to the latter answer:

:Git diff --staged

Now we're approximating the truth!

:Git pipes the output of the passed git command to the current external pager, permitting a liesurely review of all staged changes external to Vim. But there's the rub: external to Vim. That means no Vim bindings, buffers, or syntax highlighting. Ideally, we'd prefer a read-only Vim buffer syntax highlighting the output of git diff --staged. Can we do this?

The Solution

We can, or Tim Pope isn't the Vim Pope. The !-suffixed variant of :Git does just that, permitting a liesurely review of all staged changes within Vim complete with Vim-based syntax highlighting of change differences:

:Git! diff --staged

Yeah. It's pretty awesomeness.

But let's go a step farther. In the time-honoured tradition of slothful slackers everywhere, let's define a new Vim command :Greview encapsulating this operation and a new binding <leader>gr running this command. Just stash the following into your .vimrc:

command Greview :Git! diff --staged
nnoremap <leader>gr :Greview<cr>

Assuming <leader> to be ,, reviewing all staged changes reduces to ,gr. It couldn't get any Vimmier.

:Gdiff HEAD

Gdiff takes an revision argument. So you can pass it HEAD. This is not equivalent to git diff --staged, but it can serve a similar purpose.

Update: 3/28/2017,

Current version of fugitive will do this automatically when you press D on a file.

if the file is staged, only staged changes will be showed in the diff. If the file is not staged, then only change that are not staged will be visible.

As already noted, Gdiff, Gdiff : or Gdiff :0 gives you the diff with the index, Gdiff - or Gdiff HEAD gives the diff with the HEAD.

Triple-split approach

So doing a diff first with : then with - show 3 diff-panes in vim:

  1. HEAD
  2. index ("cached" or "staged")
  3. working tree
command! -bar Gvstage :Gvdiff -|Gvdiff : " vertical 3-split
command! -bar Gsstage :Gsdiff -|Gsdiff : " horizontal 3-split

Of course you can also just to Gvdiff - if you're already in diff mode.

Now pushing and getting changes is sligthly more complicated with 3 open windows, however, you can diffput from index to working tree and vice-versa easily, as on the HEAD modifiable is off, so it can never be targeted.

Otherwise, you can add some shortcuts for the diffput and diffget commands, knowing that they can take a "buffer specifier", which can be a pattern (see :help merge) or the buffer number. I modified the previous commands to save the initial buffer's number and use patterns for the others:

command! -bar Gvstage :let t:working_copy=bufnr('%')|Gvdiff -|Gvdiff : " vertical 3-split
command! -bar Gsstage :let t:working_copy=bufnr('%')|Gsdiff -|Gsdiff : " horizontal 3-split
nnoremap <Leader>hg :diffget fugitive://*/.git//[0-9a-f][0-9a-f]*/<CR> " HEAD get
nnoremap <Leader>ig :diffget fugitive://*/.git//0/<CR>                 " index get
nnoremap <Leader>ip :diffput fugitive://*/.git//0/<CR>                 " index put
nnoremap <Leader>wg :diffget <C-R>=t:working_copy<CR><CR>              " work get
nnoremap <Leader>wp :diffput <C-R>=t:working_copy<CR><CR>              " work put

Difftool approach

Alternately, if you just want a nice vimdiff view of what is staged instead of a patch, let me sugget:

command! Greview :exec "Git difftool --tool=vimdiff --staged " . fugitive#buffer().path()

This starts a new instance of vim, so when you quit it you go back to your tabs and windows you already had opened, which is perfect. This if faster (at least for me) than transitioning through the git status window, but has the drawback that you can't edit the staged file.

Use :Gtabedit @:% | Gdiff :.

This is better than the other answers because it opens in split view just like :Gdiff, rather than dumping diff syntax into a single buffer. When you're done, just :tabc to get back.

Explanation

  1. Gtabedit open a new tab and edit a fugitive object:
    • from commit @ (HEAD), a specific file :, the current file %.
  2. Gdiff diff the current buffer against another fugitive object:
    • from the index : (you can specify the file again with :% but it's not needed).

Bonus

We now have fugitive equivalents for git diff (:Gdiff) and git diff --staged (the command above). To get the behaviour of git show on the current file, use :Gtabedit @~:% | Gdiff @.

References

In case you somebody stumbled on this question.

:Gdiff --staged

will do.. :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top