Diff
Nvim:help
pages,generated fromsource using thetree-sitter-vimdoc parser.
diff-modeThis file describes the diff feature: Showing differences between two toeight versions of the same file.
The basics are explained in section
08.7 of the user manual.
To start editing in diff mode, run "nvim -d". This starts Nvim as usual, andadditionally sets up for viewing the differences between the arguments.
nvim -d file1 file2 [file3 [file4]]
In addition to the
-d argument,
-R may be used for readonly mode.
The second and following arguments may also be a directory name. Vim willthen append the file name of the first argument to the directory name to findthe file.
By default an internal diff library will be used. When
'diffopt' or
'diffexpr' has been set an external "diff" command will be used. This onlyworks when such a diff program is available.
Diffs are local to the current tab page
tab-page. You can't see diffs witha window in another tab page. This does make it possible to have severaldiffs at the same time, each in their own tab page.
What happens is that Nvim opens a window for each of the files. This is likeusing the
-O argument. This uses vertical splits, but if you preferhorizontal splits use the
-o argument instead:
nvim -d -o file1 file2 [file3 [file4]]
If you always prefer horizontal splits include "horizontal" in
'diffopt'.
In each of the edited files these options are set:
These options are set local to the window. When editing another file they arereset to the global value.The options can still be overruled from a modeline when re-editing the file.However,
'foldmethod' and
'wrap' won't be set from a modeline when
'diff' isset.See
:diffoff
for an easy way to revert the options.
The differences shown are actually the differences in the buffer. Thus if youmake changes after loading a file, these will be included in the displayeddiffs. You might have to do ":diffupdate" now and then, not all changes areimmediately taken into account, especially when using an external diff command.
In your vimrc file you could do something special when Vim was started indiff mode. You could use a construct like this:
if &diff setup for diff modeelse setup for non-diff modeendif
While already in Vim you can start diff mode in three ways.
E98:diffs[plit]
{filename}
:diffs:diffsplitOpen a new window on the file
{filename}
. The options are setas for "nvim -d" for the current and the newly opened window.Also see
'diffexpr'.
:difft:diffthis:difft[his]Make the current window part of the diff windows. This setsthe options as for "nvim -d".
:diffp[atch]
{patchfile}
E816:diffp:diffpatchUse the current buffer, patch it with the diff found in
{patchfile}
and open a buffer on the result. This sets theoptions as for "nvim -d".
{patchfile}
can be in any format that the "patch" programunderstands or
'patchexpr' can handle.Note that
{patchfile}
should only contain a diff for one file,the current file. If
{patchfile}
contains diffs for otherfiles as well, the results are unpredictable. Vim changesdirectory to /tmp to avoid files in the current directoryaccidentally being patched. But it may still result invarious ".rej" files to be created. And when absolute pathnames are present these files may get patched anyway.
To make these commands use a vertical split, prepend
:vertical. Examples:
:vert diffsplit main.c~:vert diffpatch /tmp/diff
If you always prefer a vertical split include "vertical" in
'diffopt'.
E96There can be up to eight buffers with
'diff' set.
Since the option values are remembered with the buffer, you can edit anotherfile for a moment and come back to the same file and be in diff mode again.
:diffo:diffoff:diffo[ff]Switch off diff mode for the current window. Resets relatedoptions also when
'diff' was not set.
:diffo[ff]!Switch off diff mode for the current window and in all windowsin the current tab page where
'diff' is set. Resettingrelated options only happens in a window that has
'diff' set,if the current window does not have
'diff' set then no optionsin it are changed.Hidden buffers are also removed from the list of diff'edbuffers.
The:diffoff
command resets the relevant options to the values they had whenusing:diffsplit
,:diffpatch
,:diffthis
. or starting Vim in diff mode.When using:diffoff
twice the last saved values are restored.Otherwise they are set to their default value:
'foldenable' will most-likely be reset to off. That is when
'foldmethod' isrestored to "manual". The folds themselves are not cleared but they shouldnot show up, resetting
'foldenable' is the best way to do that.
The effect is that the diff windows show the same text, with the differenceshighlighted. When scrolling the text, the
'scrollbind' option will make thetext in other windows to be scrolled as well. With vertical splits the textshould be aligned properly.
The alignment of text will go wrong when:
'wrap' is on, some lines will be wrapped and occupy two or more screen lines
folds are open in one window but not another
changes have been made to the text
"filler" is not present in
'diffopt', deleted/inserted lines makes the alignment go wrong
All the buffers edited in a window where the
'diff' option is set will join inthe diff. This is also possible for hidden buffers. They must have beenedited in a window first for this to be possible. To get rid of the hiddenbuffers use
:diffoff!
.
:DiffOrigdiff-original-fileSince
'diff' is a window-local option, it's possible to view the same bufferin diff mode in one window and "normal" in another window. It is alsopossible to view the changes you have made to a buffer since the file wasloaded. Since Vim doesn't allow having two buffers for the same file, youneed another buffer. This command is useful:
command DiffOrig vert new | set buftype=nofile | read ++edit # | 0d_ \ | diffthis | wincmd p | diffthis
Use ":DiffOrig" to see the differencesbetween the current buffer and the file it was loaded from.
A buffer that is unloaded cannot be used for the diff. But it does work forhidden buffers. You can use ":hide" to close a window without unloading thebuffer. If you don't want a buffer to remain used for the diff do ":setnodiff" before hiding it.
Vim attempts to keep the differences updated when you make changes to thetext. This mostly takes care of inserted and deleted lines. Changes within aline and more complicated changes do not cause the differences to be updated.To force the differences to be updated use:
:diffupdate
If the ! is included Vim will check if the file was changed externally andneeds to be reloaded. It will prompt for each changed file, like
:checktime
was used.
Vim will show filler lines for lines that are missing in one window but arepresent in another. These lines were inserted in another file or deleted inthis file. Removing "filler" from the
'diffopt' option will make Vim notdisplay these filler lines.
Folds are used to hide the text that wasn't changed. See
folding for allthe commands that can be used with folds.
The context of lines above a difference that are not included in the fold canbe set with the
'diffopt' option. For example, to set the context to threelines:
:set diffopt=filler,context:3
The diffs are highlighted with these groups:
hl-DiffAdd DiffAddAdded (inserted) lines. These lines exist inthis buffer but not in another.
hl-DiffChange DiffChangeChanged lines.
hl-DiffText DiffTextChanged text inside a Changed line. Exactbehavior depends on the
inline:
setting in
'diffopt'.With
inline:
set to "simple", Vim finds thefirst character that is different, and thelast character that is different (searchingfrom the end of the line). The text inbetween is highlighted. This means that partsin the middle that are still the same arehighlighted anyway. The
'diffopt' flags"iwhite" and "icase" are used here.With
inline:
set to "char" or "word", Vimuses the internal diff library to perform adetailed diff between the changed blocks andhighlight the exact difference between thetwo. Will respect any
'diffopt' flag thataffects internal diff.Not used when
inline:
is set to "none".
hl-DiffTextAdd DiffTextAddAdded text inside a Changed line. Similar toDiffText, but used when there is nocorresponding text in other buffers. Not usedwhen
inline:
is set to "simple" or "none".
hl-DiffDelete DiffDeleteDeleted lines. Also called filler lines,because they don't really exist in thisbuffer.
Two commands can be used to jump to diffs:
[c[cJump backwards to the previous start of a change.When a count is used, do it that many times.
]c]cJump forwards to the next start of a change.When a count is used, do it that many times.
It is an error if there is no change for the cursor to move to.
mergeThere are two commands to copy text from one buffer to another. The result isthat the buffers will be equal within the specified range.
:diffg:diffget:[range]diffg[et] [bufspec]Modify the current buffer to undo difference with anotherbuffer. If [bufspec] is given, that buffer is used. If[bufspec] refers to the current buffer then nothing happens.Otherwise this only works if there is one other buffer in diffmode.See below for [range].
:diffpu:diffputE793:[range]diffpu[t] [bufspec]Modify another buffer to undo difference with the currentbuffer. Just like ":diffget" but the other buffer is modifiedinstead of the current one.When [bufspec] is omitted and there is more than one otherbuffer in diff mode where
'modifiable' is set this fails.See below for [range].
do[count]doSame as ":diffget" without range. The "o" stands for "obtain"("dg" can't be used, it could be the start of "dgg"!).
Note:this doesn't work in Visual mode.If you give a [count], it is used as the [bufspec] argumentfor ":diffget".
dp[count]dpSame as ":diffput" without range.
Note: this doesn't work inVisual mode.If you give a [count], it is used as the [bufspec] argumentfor ":diffput".
When no [range] is given, the diff at the cursor position or just above it isaffected. There can be deleted lines below the last line of the buffer. Whenthe cursor is on the last line in the buffer and there is no diff above thisline, and no [range] is given, the diff below the cursor position will be usedinstead.
When [range] is used, Vim tries to only put or get the specified lines. Whenthere are deleted lines, they will be used if they are between the linesspecified by [range].
To be able to put or get those lines to/from another buffer in a [range] it'sallowed to use 0 and the last line number plus one. This command gets alldiffs from the other buffer:
:0,$+1diffget
Note that deleted lines are displayed, but not counted as text lines. Youcan't move the cursor into them. To fill the deleted lines with the linesfrom another buffer use ":diffget" on the line below them.
E787When the buffer that is about to be modified is read-only and the autocommandthat is triggered by
FileChangedRO changes buffers the command will fail.The autocommand must not change buffers.
The [bufspec] argument above can be a buffer number, a pattern for a buffername or a part of a buffer name. Examples:
:diffgetUse the other buffer which is in diff mode:diffget 3Use buffer 3:diffget v2Use the buffer which matches "v2" and is indiff mode (e.g., "file.c.v2")
Diff anchors allow you to control where the diff algorithm aligns andsynchronize text across files. Each anchor matches each other in each file,allowing you to control the output of a diff.
This is useful when a change involves complicated edits. For example, if afunction was moved to another location and further edited. By default, thealgorithm aims to create the smallest diff, which results in that entirefunction being considered to be deleted and added on the other side, making ithard to see what the actual edit on it was. You can use diff anchors to pinthat function so the diff algorithm will align based on it.
To use it, set anchors using
'diffanchors' which is a comma-separated list of
{address}
in each file, and then add "anchor" to
'diffopt'. Internally, Vimsplits each file up into sections split by the anchors. It performs the diffon each pair of sections separately before merging the results back.
Setting
'diffanchors' will update the diff immediately. If an anchor is tiedto a mark, and you change what the mark is pointed to, you need to manuallycall
:diffupdate afterwards to get the updated diff results.
Example:
Let's say we have the following files, side-by-side. We are interested in thechange that happened to the functionfoo()
, which was both edited and moved.
File A:
int foo() { int n = 1; return n;}int g = 1;int bar(int a) { a *= 2; a += 3; return a;}
<File B:
int bar(int a) { a *= 2; a += 3; return a;}int foo() { int n = 999; return n;}int g = 1;
A normal diff will usually align the diff result as such:
int foo() { |---------------- int n = 1; |---------------- return n; |----------------} |---------------- |----------------int g = 1; |---------------- |----------------int bar(int a) {|int bar(int a) { a *= 2; | a *= 2; a += 3; | a += 3; return a; | return a;} |}----------------|----------------|int foo() {----------------| int n = 999;----------------| return n;----------------|}----------------|----------------|int g = 1;
What we want is to instead ask the diff to align on
foo()
:
----------------|int bar(int a) {----------------| a *= 2;----------------| a += 3;----------------| return a;----------------|}----------------|int foo() { |int foo() { int n = 1; | int n = 999; return n; | return n;} |} |int g = 1; |int g = 1; |----------------int bar(int a) {|---------------- a *= 2; |---------------- a += 3; |---------------- return a; |----------------} |----------------
Below are some ways of setting diff anchors to get the above result. In eachexample,
'diffopt' needs to have
anchor
set for this to take effect.
Marks: Set the
'a mark on the
int foo()
lines in each file first beforesetting the anchors:
set diffanchors='a
Pattern: Specify the anchor using a
pattern (see
:/). Here, we make sureto always start search from line 1 for consistency:
set diffanchors=1/int\ foo(/
Selection: Use visual mode to select the entire
foo()
function body in eachfile. Here, we use two anchors. This does a better job of making sure onlythe function bodies are anchored against each other but not the lines afterit. Note the
'>+1
below. The "+1" is necessary as we want the split tohappen below the last line of the function, not above:
set diffanchors='<,'>+1
Manually set two anchors using line numbers via buffer-local options:
setlocal diffanchors=1,5wincmd wsetlocal diffanchors=7,11
diff-slowdiff_translationsFor very long lines, the diff syntax highlighting might be slow, especiallysince it tries to match all different kind of localisations. To disablelocalisations and speed up the syntax highlighting, set the global variableg:diff_translations to zero:
let g:diff_translations = 0
After setting this variable, reload the syntax script:
set syntax=diff
The
'diffexpr' option can be set to use something else than the internal diffsupport or the standard "diff" program to compare two files and find thedifferences.
When
'diffexpr' is empty, Vim uses this command to find the differencesbetween file1 and file2:
diff file1 file2 > outfile
The ">" is replaced with the value of
'shellredir'.
The output of "diff" must be a normal "ed" style diff or a unified diff. Acontext diff will NOT work. For a unified diff no context lines can be used.Using "diff -u" will NOT work, use "diff -U0".
This example explains the format that Vim expects for the "ed" style diff:
1a2> bbb4d4< 1117c7< GGG---> ggg
The "1a2" item appends the line "bbb".The "4d4" item deletes the line "111".The "7c7" item replaces the line "GGG" with "ggg".
When
'diffexpr' is not empty, Vim evaluates it to obtain a diff file in theformat mentioned. These variables are set to the file names used:
v:fname_inoriginal filev:fname_newnew version of the same filev:fname_outwhere to write the resulting diff file
The advantage of using a function call without arguments is that it is faster,see
expr-option-function.
Example (this does almost the same as
'diffexpr' being empty):
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
The "-a" argument is used to force comparing the files as text, comparing asbinaries isn't useful. The "--binary" argument makes the files read in binarymode, so that a
CTRL-Z
doesn't end the text on DOS.
Theredraw!
command may not be needed, depending on whether executing ashell command shows something on the display or not.
If the
'diffexpr' expression starts with s: or
<SID>, then it is replacedwith the script ID (
local-function). Example:
set diffexpr=s:MyDiffExpr()set diffexpr=<SID>SomeDiffExpr()
Otherwise, the expression is evaluated in the context of the script where theoption was set, thus script-local items are available.
E810E97Vim will do a test if the diff output looks alright. If it doesn't, you willget an error message. Possible causes:
The "diff" program cannot be executed.
The "diff" program doesn't produce normal "ed" style diffs (see above).
The
'shell' and associated options are not set correctly. Try if filtering works with a command like ":!sort".
You are using
'diffexpr' and it doesn't work.If it's not clear what the problem is set the
'verbose' option to one or moreto see more messages.
The
'patchexpr' option can be set to use something else than the standard"patch" program.
When
'patchexpr' is empty, Vim will call the "patch" program like this:
patch -o outfile origfile < patchfile
This should work fine with most versions of the "patch" program. Note that aCR in the middle of a line may cause problems, it is seen as a line break.
If the default doesn't work for you, set the
'patchexpr' to an expression thatwill have the same effect. These variables are set to the file names used:
v:fname_inoriginal filev:fname_diffpatch filev:fname_outresulting patched file
The advantage of using a function call without arguments is that it is faster,see
expr-option-function.
Example (this does the same as
'patchexpr' being empty):
set patchexpr=MyPatch()function MyPatch() :call system("patch -o " .. v:fname_out .. " " .. v:fname_in .. \ " < " .. v:fname_diff)endfunction
Make sure that using the "patch" program doesn't have unwanted side effects.For example, watch out for additionally generated files, which should bedeleted. It should just patch the file and nothing else. Vim will change directory to "/tmp" or another temp directory beforeevaluating
'patchexpr'. This hopefully avoids that files in the currentdirectory are accidentally patched. Vim will also delete files starting withv:fname_in and ending in ".rej" and ".orig".
If the
'patchexpr' expression starts with s: or
<SID>, then it is replacedwith the script ID (
local-function). Example:
set patchexpr=s:MyPatchExpr()set patchexpr=<SID>SomePatchExpr()
Otherwise, the expression is evaluated in the context of the script where theoption was set, thus script-local items are available.