如何在Fugitive中运行`git diff --staged`命令?

33

:Gdiff命令与在该文件上运行git diff相当。

git diff --stagedgit diff --cached的等效命令是什么?


如果我再问这个问题的话,我会在 https://vi.stackexchange.com 上问。 - Flimm
8个回答

38
我找到了一种方法来实现这个。运行:Git命令,你会看到一个窗口,里面的内容如下:
# Head: master
# Merge: origin/master
# Help: g?
#
# Staged (1)
# M example.txt
#

向下滚动到暂存文件,example.txt,然后按下 dd。这将打开一个差异视图,比较在HEAD和索引中的内容。您会注意到底部的栏上两个文件名都是特殊的Fugitive文件名。

此外,在 :Git 预览窗口中,您可以按下 g?,它将列出当前上下文中有效的所有映射。


"Gstatus D已被弃用,建议使用dd代替。" - jmm
@jmm 是的,我也收到了那个消息,但是它是什么意思呢?在正常模式下,dd 删除一行。而 :Gstatus 后跟 dd 则会执行不同的操作。 - Konrad Rudolph
1
@KonradRudolph 运行 :Gstatus(现已弃用,改为使用 :Git),在光标位于要查看差异的文件上方时键入 dd。请参阅 :help fugitive-maps 以获取更多在 :Git 缓冲区中使用的按键绑定。 - Jonathan Barber

23
vim-fugitive没有提供直接对应git diff --stagedgit diff --cached的功能,但它提供了一个通用的Vim命令:Git!,可以将任意git命令的输出导入只读Vim缓冲区。在此之前,我们需要明确重述问题:git diff --stagedgit diff --cached是相同操作的同义词:对比索引(所有暂存更改的集合)与HEAD(当前分支的最新提交)的内容,通常用于在提交前审核更改。因此,所述问题变成了:“vim-fugitive中审查所有已暂存更改的最有效方法是什么?”很明显,目前被接受的自我回答未能解决这个问题。排名第二的自我回答也不是更好的选择。 :Gstatus 绑定仅适用于当前行的文件,因此根据定义,无法用于查看所有已暂存的更改。此外,:Gstatus D 绑定甚至不会为当前行的文件审查所有已暂存的更改。它只会对该文件的索引和工作树副本进行差异比较,而不是对该文件的索引和最近提交的副本进行差异比较(这是完全不同的情况)。 :Gdiff HEAD 同样不适用。它只会对与当前缓冲区对应的文件的最近提交和工作树副本进行差异比较。没有参数的 :Gdiff 等效于 :Gstatus D 绑定,再次对该文件的索引和工作树副本进行差异比较。两者都不会审查所有已暂存的更改。

适用的答案

emaniacs 在对后一个答案的评论中接近了可行的解决方案:

:Git diff --staged

现在我们正在逼近真相!

:Git 将传递的 git 命令的输出管道传输到当前外部分页器,允许轻松查看所有在 Vim 外部暂存的更改。但是问题在于:在 Vim 外部。这意味着没有 Vim 绑定、缓冲区或语法高亮。理想情况下,我们希望一个只读的 Vim 缓冲区语法高亮显示 git diff --staged 的输出。我们能做到吗?

解决方案

我们可以做到,否则 Tim Pope 不会成为 Vim 教皇。带有 ! 后缀的 :Git 正是如此,允许在 Vim 中悠闲地查看所有暂存更改,并且带有基于 Vim 的语法高亮显示更改差异:

:Git! diff --staged

是的。它非常棒。

但是让我们再进一步。按照懒惰人的惯例,让我们定义一个新的Vim命令:Greview来封装这个操作,并且定义一个新的绑定<leader>gr来运行这个命令。只需要将以下代码添加到您的.vimrc文件中:

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

假设<leader>,,那么审查所有已暂存的更改就变成了,gr。这再也不能更Vim了。

这个命令 :Greview 必须vim-fugitive 的一部分! - jackyalcine
这很棒,可以在一个缓冲区中查看所有更改,谢谢!是否有一种简单的方法可以在单独的选项卡中打开每个暂存文件的差异视图? - Achal Dave
嗨!我不知道如何以简单易行的方式从那个tmp文件中返回,所以我的解决方法是 command Greview :Gtabedit! diff --staged - ssaid
需要注意的是,对于单个文件,这与该行上的“dd”相同。 - TamaMcGlinn

15
:Gdiff HEAD

Gdiff需要一个修订参数。因此,您可以将HEAD传递给它。这与git diff --staged不等效,但它可以起到类似的作用。


另一种方法是使用 :Git diff --staged,因为 :Git 等同于 git 命令。 - emaniacs
2
@emaniacs:这并不像:Gdiff那样使用Vim的差异功能打开差异。 - Flimm

2
使用:Gtabedit @:% | Gdiff :
这比其他答案更好,因为它像:Gdiff一样在分割视图中打开,而不是将diff语法转储到单个缓冲区中。完成后,只需使用:tabc返回。
解释:
  1. Gtabedit打开一个新的标签页并编辑fugitive对象:
    • 从提交@(HEAD),指定文件:,当前文件%
  2. Gdiff将当前缓冲区与另一个fugitive对象进行比较:
    • 从索引:(您可以再次使用:%指定文件,但不需要)。
奖励:
我们现在有了git diff:Gdiff)和git diff --staged(上面的命令)的fugitive等效物。要在当前文件上获得git show的行为,请使用:Gtabedit @~:% | Gdiff @
参考资料:

2
更新:2017年3月28日,
当您按下文件上的 D 键时,当前版本的fugitive会自动执行此操作。
如果文件已暂存,则仅显示已暂存更改的差异。 如果文件未暂存,则仅可见未暂存的更改。

2

如前所述,GdiffGdiff :Gdiff :0将为您提供与索引的差异, Gdiff -Gdiff HEAD将为您提供与HEAD的差异。

三分法

因此,首先使用:进行差异处理,然后再使用-在vim中显示3个差异面板:

  1. HEAD
  2. 索引(“缓存”或“暂存”)
  3. 工作树
command! -bar Gvstage :Gvdiff -|Gvdiff : " vertical 3-split
command! -bar Gsstage :Gsdiff -|Gsdiff : " horizontal 3-split

当然,如果您已经进入diff模式,也可以直接使用Gvdiff -
现在,在有3个打开的窗口时,推送和获取更改会稍微复杂一些。不过,您可以轻松地从索引到工作树中进行diffput操作,反之亦然。由于HEAD上的modifiable是关闭的,因此它永远不可能被定位。
否则,您可以为diffputdiffget命令添加一些快捷方式。请注意,它们可以接受“缓冲区指定符”,该指定符可以是一个模式(参见:help merge)或缓冲区编号。我修改了以前的命令,保存了初始缓冲区的编号,并对其他内容使用了模式。
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

差异工具方法

或者,如果您只想要一个漂亮的vimdiff视图来查看已暂存的内容而不是补丁,请让我建议:

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

这将启动一个新的vim实例,因此当您退出它时,您会回到您已经打开的标签和窗口,这是非常完美的。对我来说,这比通过git状态窗口进行过渡更快,但缺点是您无法编辑暂存的文件。


1

简述:

Gtabedit :0 | Gdiffsplit @:#


说明:

当您同时具有暂存更改和未暂存更改时,有3个相关的差异。以下分别使用 gitvim-fugitive 显示了每个差异。所有vim-fugitive 命令都在当前编辑器会话中打开实际的 vim diff。

NB: Gdiffsplit HEAD 的行为类似于 git diff HEAD -- <current_file>,而不是 git diff --staged <current_file>

  1. 比较最后一次提交和暂存更改之间的差异,不包括未暂存的更改。即如果您现在运行 git commit(不带 -a),实际上会提交什么。这是原始问题的答案:

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

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

    (这里的 2 个 vim-fugitive 变体只是更改了窗口的顺序)

  2. 比较工作目录(未暂存)和暂存更改之间的差异。即如果您现在运行 git add <current_file>,会将什么添加到索引中:

    git diff <current_file>

    Gdiffsplit

  3. 比较所有更改(已暂存和未暂存)与最后一次提交之间的差异。即如果您现在运行 git commit -a,会提交什么:

    git diff HEAD -- x

    Gdiffsplit HEAD

注意:上面的git commit命令将包括其他已跟踪的文件。

注意:为什么需要另一个答案?

  • 之前的答案将git diff --stagedgit diff HEAD混淆了(当您有暂存和未暂存的更改时,可以看到这一点)
  • 此答案显示如何在当前vim会话中打开实际的vim差异窗口,以每种类型的差异(即最初提出的GdiffGdiffsplit的实际行为)

-3

如果有人偶然发现了这个问题。

:Gdiff --staged

会起作用的.. :)


1
恐怕这对我不起作用。但它确实帮我找到了正确的答案,所以感谢您的帮助! - Flimm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接