差异
Nvim 的 :help
页面,生成 自 源代码,使用 tree-sitter-vimdoc 解析器。
diff 模式 此文件描述了 diff 功能:显示同一个文件的两到八个版本的差异。
要以 diff 模式开始编辑,运行 "nvim -d"。这将像往常一样启动 Nvim,并额外设置以查看参数之间的差异。
nvim -d file1 file2 [file3 [file4]]
除了
-d 参数外,
-R 可用于只读模式。
第二个及以后的参数也可以是目录名称。Vim 将在目录名称后面追加第一个参数的文件名以查找该文件。
差异是当前标签页
标签页 的本地差异。你无法在另一个标签页的窗口中看到差异。这确实使得一次拥有多个差异成为可能,每个差异都在自己的标签页中。
发生的情况是,Nvim 为每个文件打开一个窗口。这就像使用
-O 参数一样。这使用垂直分割,但如果你更喜欢水平分割,请改用
-o 参数。
nvim -d -o file1 file2 [file3 [file4]]
如果你始终更喜欢水平分割,请在
'diffopt' 中包含 "horizontal"。
在每个被编辑的文件中,以下选项被设置
这些选项在窗口级别被设置。当编辑另一个文件时,它们将重置为全局值。选项仍然可以从模式行中被覆盖,当再次编辑文件时。但是,当
'diff' 被设置时,
'foldmethod' 和
'wrap' 不会从模式行中被设置。请参阅
:diffoff
,了解轻松恢复选项的方法。
显示的差异实际上是缓冲区中的差异。因此,如果你在加载文件后进行修改,这些修改将包含在显示的差异中。你可能需要偶尔执行 ":diffupdate",因为并非所有修改都会立即生效,尤其是在使用外部 diff 命令时。
在你的 vimrc 文件中,你可以做一些特殊的事情,当 Vim 以 diff 模式启动时。你可以使用类似下面的结构
if &diff
setup for diff mode
else
setup for non-diff mode
endif
当已经在 Vim 中时,你可以通过三种方式启动 diff 模式。
:diffp[atch]
{patchfile}
E816 :diffp :diffpatch 使用当前缓冲区,用
{patchfile}
中找到的 diff 对其进行补丁,并在结果上打开一个缓冲区。这将设置与 "nvim -d" 相同的选项。
{patchfile}
可以是 "patch" 程序理解的任何格式,或者
'patchexpr' 可以处理的格式。请注意,
{patchfile}
应该只包含一个文件的 diff,即当前文件。如果
{patchfile}
还包含其他文件的 diff,结果将不可预测。Vim 会将目录更改为 /tmp 以避免意外地对当前目录中的文件进行补丁。但它仍然可能导致各种 ".rej" 文件被创建。当存在绝对路径名时,这些文件可能仍然会被补丁。
要使这些命令使用垂直分割,请在前面添加
:vertical。示例
:vert diffsplit main.c~
:vert diffpatch /tmp/diff
如果你始终更喜欢垂直分割,请在
'diffopt' 中包含 "vertical"。
由于选项值与缓冲区一起被记忆,你可以短暂地编辑另一个文件,然后回到同一个文件,并再次进入 diff 模式。
:diffo[ff]! 关闭当前窗口的 diff 模式,并在当前标签页中所有设置了
'diff' 的窗口中关闭 diff 模式。重置相关选项仅在设置了
'diff' 的窗口中进行,如果当前窗口未设置
'diff',则不会更改其中的任何选项。隐藏缓冲区也从 diff 缓冲区列表中删除。
:diffoff
命令将相关选项重置为使用 :diffsplit
、:diffpatch
、:diffthis
或以 diff 模式启动 Vim 时所具有的值。当两次使用 :diffoff
时,将恢复上次保存的值。否则,它们将被设置为其默认值。
效果是 diff 窗口显示相同的文本,差异用高亮显示。当滚动文本时,
'scrollbind' 选项将使其他窗口中的文本也滚动。对于垂直分割,文本应该对齐良好。
当出现以下情况时,文本对齐会出错
在一个窗口中折叠打开,但在另一个窗口中没有打开
文本已发生更改
所有在设置了
'diff' 选项的窗口中编辑的缓冲区都将加入 diff。这对于隐藏缓冲区也是可能的。它们必须先在一个窗口中被编辑,才能实现。要摆脱隐藏缓冲区,请使用
:diffoff!
。
:DiffOrig diff-原始文件 由于
'diff' 是一个窗口本地选项,因此可以在一个窗口中以 diff 模式查看同一个缓冲区,而在另一个窗口中以 "正常" 模式查看。也可以查看自加载文件以来对缓冲区所做的更改。由于 Vim 不允许为同一个文件拥有两个缓冲区,因此你需要另一个缓冲区。此命令很有用
command DiffOrig vert new | set buftype=nofile | read ++edit # | 0d_
\ | diffthis | wincmd p | diffthis
使用 ":DiffOrig" 查看当前缓冲区与其加载来源文件之间的差异。
一个未加载的缓冲区不能用于 diff。但它确实适用于隐藏缓冲区。你可以使用 ":hide" 关闭一个窗口,而不卸载缓冲区。如果你不希望缓冲区仍然用于 diff,请在隐藏它之前执行 ":set nodiff"。
当你对文本进行更改时,Vim 会尝试保持差异更新。这主要处理插入和删除的线路。线路内的更改和更复杂的更改不会导致差异更新。要强制更新差异,请使用
:diffupdate
如果包含 !,Vim 将检查文件是否在外部被修改,是否需要重新加载。它将提示每个修改的文件,就像使用
:checktime
一样。
Vim 将显示填充行,用于在一个窗口中缺失但在另一个窗口中存在的行。这些行是在另一个文件中插入的,或者是在此文件中删除的。从
'diffopt' 选项中移除 "filler" 将使 Vim 不显示这些填充行。
折叠用于隐藏未更改的文本。请参阅
折叠,了解所有可用于折叠的命令。
差异上方未包含在折叠中的行的上下文可以使用
'diffopt' 选项设置。例如,要将上下文设置为三行
:set diffopt=filler,context:3
差异用以下组进行高亮显示
hl-DiffAdd DiffAdd 添加(插入)的线路。这些线路存在于此缓冲区中,但在另一个缓冲区中不存在。
hl-DiffChange DiffChange 更改的线路。
hl-DiffText DiffText 更改的线路内的更改文本。Vim 会找到第一个不同的字符,以及最后一个不同的字符(从行尾搜索)。它们之间的文本将被高亮显示。这意味着中间仍然相同的部分也会被高亮显示。这里的
'diffopt' 标志 "iwhite" 和 "icase" 被使用。
hl-DiffDelete DiffDelete 删除的线路。也称为填充行,因为它们实际上不存在于此缓冲区中。
可以使用两个命令跳至差异:
[c[c 向后跳至上一个更改的开始。当使用计数时,重复执行该操作该次数。
]c]c 向前跳至下一个更改的开始。当使用计数时,重复执行该操作该次数。
如果游标没有可以移动到的更改,则会出错。
合并 有两个命令可以将文本从一个缓冲区复制到另一个缓冲区。结果是,缓冲区在指定范围内将相等。
:diffg :diffget :[range]diffg[et] [bufspec] 修改当前缓冲区以撤消与另一个缓冲区的差异。如果给出 [bufspec],则使用该缓冲区。如果 [bufspec] 指向当前缓冲区,则不会发生任何事情。否则,只有在 diff 模式下还有另一个缓冲区时,此命令才会生效。请参阅下方了解 [range]。
:diffpu :diffput E793 :[range]diffpu[t] [bufspec] 修改另一个缓冲区以撤消与当前缓冲区的差异。与“:diffget”相同,但修改的是另一个缓冲区,而不是当前缓冲区。当[bufspec]被省略,并且在 diff 模式下有多个其他缓冲区
'modifiable' 被设置,则会失败。有关[range],请参见下文。
do [count]do 与“:diffget”相同,没有范围。 “o”代表“获得”(“dg”不能使用,它可能是“dgg”的开始!)。
注意:这在 Visual 模式下不起作用。如果你提供一个[count],它将被用作“:diffget”的[bufspec]参数。
dp [count]dp 与“:diffput”相同,没有范围。
注意:这在 Visual 模式下不起作用。如果你提供一个[count],它将被用作“:diffput”的[bufspec]参数。
如果没有提供[range],则会影响光标位置或其上方的差异。当使用[range]时,Vim 会尝试仅放置或获取指定的行。当存在已删除的行时,这可能并不总是可行。
在缓冲区的最后一行下方可能存在已删除的行。当光标位于缓冲区的最后一行,并且该行上方没有差异时,“:diffget”和“do”命令将从另一个缓冲区获取行。
为了能够在[range]中从另一个缓冲区获取这些行,可以使用最后一行号加一。此命令将获取来自另一个缓冲区的所有差异
:1,$+1diffget
请注意,已删除的行将显示,但不会被计为文本行。你无法将光标移动到其中。要使用来自另一个缓冲区的行填充已删除的行,请在它们下方的行上使用“:diffget”。
E787当即将被修改的缓冲区为只读,并且由
FileChangedRO触发的自动命令更改缓冲区时,命令将失败。自动命令不得更改缓冲区。
上面的[bufspec]参数可以是缓冲区编号、缓冲区名称的模式或缓冲区名称的一部分。示例
:diffget 使用另一个处于 diff 模式的缓冲区 :diffget 3 使用缓冲区 3 :diffget v2 使用与“v2”匹配并处于 diff 模式的缓冲区(例如,“file.c.v2”)
diff-slow diff_translations 对于非常长的行,diff 语法高亮可能会很慢,特别是因为它会尝试匹配所有不同类型的本地化。要禁用本地化并加快语法高亮速度,请将全局变量 g:diff_translations 设置为零
let g:diff_translations = 0
设置此变量后,重新加载语法脚本
set syntax=diff
可以将
'diffexpr' 选项设置为使用内部 diff 支持或标准“diff”程序以外的其他内容来比较两个文件并找到差异。
“diff”的输出必须是标准的“ed”样式 diff 或统一 diff。上下文 diff 将不起作用。对于统一 diff,不能使用上下文行。使用“diff -u”将不起作用,请使用“diff -U0”。
此示例说明了 Vim 期望的“ed”样式 diff 格式
1a2
> bbb
4d4
< 111
7c7
< GGG
---
> ggg
“1a2”项追加行“bbb”。“4d4”项删除行“111”。“7c7”项将行“GGG”替换为“ggg”。
当
'diffexpr' 不为空时,Vim 会对其进行求值以获得上面提到的格式的 diff 文件。这些变量被设置为使用的文件名
v:fname_in 原始文件 v:fname_new 同一文件的最新版本 v:fname_out 将结果 diff 文件写入的位置
示例(这与
'diffexpr' 为空几乎相同)
set diffexpr=MyDiff()
function MyDiff()
let opt = ""
if &diffopt =~ "icase"
let opt = opt .. "-i "
endif
if &diffopt =~ "iwhite"
let opt = opt .. "-b "
endif
silent execute "!diff -a --binary " .. opt .. v:fname_in .. " " .. v:fname_new ..
\ " > " .. v:fname_out
redraw!
endfunction
“-a”参数用于强制将文件作为文本进行比较,作为二进制文件进行比较没有用。 “--binary”参数使文件以二进制模式读取,这样
CTRL-Z
不会在 DOS 上结束文本。
redraw!
命令可能不需要,这取决于执行 shell 命令是否在显示屏上显示内容。
如果
'diffexpr' 表达式以 s: 或
<SID> 开头,则将其替换为脚本 ID (
local-function)。示例
set diffexpr=s:MyDiffExpr()
set diffexpr=<SID>SomeDiffExpr()
否则,表达式将在设置选项的脚本的上下文中求值,因此脚本本地项目可用。
E810 E97 Vim 将进行测试以查看 diff 输出是否正常。如果它没有,你将收到一条错误消息。可能原因
无法执行“diff”程序。
“diff”程序没有生成标准的“ed”样式 diff(见上文)。
'shell' 和相关选项设置不正确。尝试使用“:!sort”之类的命令来查看过滤是否有效。
当
'patchexpr' 为空时,Vim 将像这样调用“patch”程序
patch -o outfile origfile < patchfile
这应该适用于大多数版本的“patch”程序。请注意,行中间的 CR 可能会导致问题,它被视为换行符。
v:fname_in 原始文件 v:fname_diff 补丁文件 v:fname_out 结果的修补文件
示例(这与
'patchexpr' 为空相同)
set patchexpr=MyPatch()
function MyPatch()
:call system("patch -o " .. v:fname_out .. " " .. v:fname_in ..
\ " < " .. v:fname_diff)
endfunction
确保使用“patch”程序没有不必要的副作用。例如,注意额外生成的应该删除的文件。它应该只修补文件,而不是其他任何东西。Vim 在求值
'patchexpr' 之前将更改目录到“/tmp”或另一个临时目录。这希望能避免意外修补当前目录中的文件。Vim 还将删除以 v:fname_in 开头并以“.rej”和“.orig”结尾的文件。
如果
'patchexpr' 表达式以 s: 或
<SID> 开头,则将其替换为脚本 ID (
local-function)。示例
set patchexpr=s:MyPatchExpr()
set patchexpr=<SID>SomePatchExpr()
否则,表达式将在设置选项的脚本的上下文中求值,因此脚本本地项目可用。