How can I run `git diff --staged` with Fugitive?
Asked Answered
P

8

33

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

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

Prisca answered 14/3, 2013 at 11:13 Comment(1)
If I were to this ask this question again, I would ask it on vi.stackexchange.comPrisca
P
38

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

# Head: master
# Merge: origin/master
# Help: g?
#
# Staged (1)
# M example.txt
#

Scroll down to the staged file, example.txt, and press dd. 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 :Git preview window, you can press g?, which will list all the mappings valid in the current context.

Prisca answered 22/8, 2014 at 8:57 Comment(3)
:Gstatus D is deprecated in favor of ddLeif
@Leif Yes I got that message too, but what does it mean? dd in normal mode deletes a line. And :Gstatus followed by dd does something different.Hamadryad
@KonradRudolph run :Gstatus (now deprecated for :Git) and type dd whilst the cursor is on top of the file you want to view the diff for. See :help fugitive-maps for more keybindings to use in the :Git buffer.Soliloquy
H
23

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.

Harrell answered 5/4, 2015 at 5:35 Comment(4)
This command :Greview needs to be part of vim-fugitive!Leptophyllous
This is great for seeing all the changes in one buffer, thanks! Is there an easy way to open each staged file in diff view in its own separate tab?Ossification
Hi! I don't know how to get back from that tmp file in an easy and simple way, so my workaround was command Greview :Gtabedit! diff --stagedStgermain
It should be noted that, for a single file, this is the same as dd on that line.Tang
P
15
: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.

Prisca answered 18/7, 2013 at 10:22 Comment(2)
The other way is using :Git diff --staged, because :Git is equivalent with git command.Macintosh
@emaniacs: this doesn't open the diff using Vim's diff functionality like :Gdiff does.Prisca
M
2

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.

Macfadyn answered 29/3, 2017 at 3:37 Comment(0)
H
2

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.

Hardecanute answered 10/11, 2017 at 19:16 Comment(0)
U
2

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

Uni answered 14/12, 2018 at 21:32 Comment(0)
B
1

TLDR:

Gtabedit :0 | Gdiffsplit @:#


Explanation:

There are 3 relevant diffs when you have both staged changes and unstaged changes. The following shows each of those with git and vim-fugitive. The vim-fugitive commands all open an actual vim diff in the current editor session.

NB: Gdiffsplit HEAD behaves like git diff HEAD -- <current_file>, not like git diff --staged <current_file>.

  1. Diff between last commit and staged changes, excluding unstaged changes. i.e. What would actually get committed if you ran git commit right now (without -a). This is the answer to the original question as it is written:

    git diff --staged <current_file> or git diff --cached <current_file>

    Gtabedit :0 | Gdiffsplit @:# or Gtabedit @:% | Gdiffsplit :0

    (The 2 vim-fugitive variants here just change the order of the windows)

  2. Diff between working dir (unstaged) and staged changes. i.e. What would get added to index if you ran git add <current_file> right now:

    git diff <current_file>

    Gdiffsplit

  3. Diff between all changes (staged and unstaged) and the last commit. i.e. What would get committed if you ran git commit -a right now:

    git diff HEAD -- x

    Gdiffsplit HEAD

NB: git commit commands above would include other tracked files


NOTE: Why is yet another answer needed?

  • Previous answers confuse git diff --staged with git diff HEAD (this can be seen when you have staged and unstaged changes)
  • This answer shows how to open an actual vim diff window in the current vim session for each type of diff (i.e. the actual behavior of Gdiff i.e. Gdiffsplit as originally asked)
Bhakti answered 13/4, 2023 at 18:43 Comment(0)
S
-3

In case you somebody stumbled on this question.

:Gdiff --staged

will do.. :)

Spadefish answered 18/7, 2013 at 9:58 Comment(1)
I'm afraid this doesn't work for me. But it did help me find the correct answer, so thanks for the help!Prisca

© 2022 - 2024 — McMap. All rights reserved.