Neovim’s diff mode allows you to view and merge differences between two to eight files simultaneously, making it an excellent tool for code reviews and conflict resolution.
Starting Diff Mode
From the Command Line
Two Files
Three-Way Merge
With Directory
Horizontal Split
nvim -d file1.txt file2.txt
nvim -d base.txt theirs.txt mine.txt
nvim -d file.txt /path/to/dir/
# Neovim looks for file.txt in /path/to/dir/
nvim -d -o file1.txt file2.txt
By default, diff mode uses vertical splits. Add -o for horizontal splits, or set set diffopt+=horizontal in your config.
From Within Neovim
:diffsplit file.txt " Open file.txt in diff mode
:vert diffsplit file.txt " Vertical split (default)
:diffthis " Make current window part of diff
:diffpatch patch.diff " Apply patch and show diff
Diff Window Options
When diff mode starts, these options are automatically set:
'diff' = on " Enable diff mode
'scrollbind' = on " Sync scrolling
'cursorbind' = on " Sync cursor position
'foldmethod' = diff " Fold unchanged lines
'foldcolumn' = 2 " Show fold column
'wrap' = off " Disable line wrapping
'scrollopt' += hor " Horizontal scrolling
Navigating Differences
Jump Between Changes
]c " Jump to next change
[c " Jump to previous change
[c " With count: 3]c jumps to 3rd next change
These commands work like ]s and [s for spell checking.
Merging Changes
Obtain and Put Changes
do " Obtain diff from other window
:diffget " Same as 'do'
:diffget 2 " Get from buffer number 2
:diffget //2 " Get from window 2 (3-way merge)
dp " Put diff to other window
:diffput " Same as 'dp'
:diffput 3 " Put to buffer number 3
do stands for “diff obtain” (not “diff open”) because dg would conflict with dgg.
Range Operations
:5,10diffget " Obtain lines 5-10 from other window
:20,30diffput " Put lines 20-30 to other window
:0,$+1diffget " Get all differences
Diff Highlighting
Differences are highlighted with these groups:
| Group | Description |
|---|
DiffAdd | Lines added (only in this buffer) |
DiffChange | Lines changed |
DiffText | Exact changed text within a line |
DiffDelete | Lines deleted (filler lines) |
DiffTextAdd | Added text (when inline: is not “simple”) |
Customize Diff Colors
" For light backgrounds
highlight DiffAdd guibg=#d0ffd0 guifg=NONE
highlight DiffChange guibg=#ffffcc guifg=NONE
highlight DiffDelete guibg=#ffcccc guifg=NONE
highlight DiffText guibg=#ffff80 guifg=NONE
" For dark backgrounds
highlight DiffAdd guibg=#1a3a1a guifg=NONE
highlight DiffChange guibg=#3a3a1a guifg=NONE
highlight DiffDelete guibg=#3a1a1a guifg=NONE
highlight DiffText guibg=#4a4a1a guifg=NONE
Diff Options
:set diffopt=filler,context:3 " Show 3 lines of context
:set diffopt+=iwhite " Ignore whitespace
:set diffopt+=algorithm:patience " Use patience diff algorithm
:set diffopt+=indent-heuristic " Better indentation alignment
:set diffopt+=vertical " Always use vertical splits
Common diffopt flags:
| Flag | Effect |
|---|
filler | Show filler lines for deleted lines |
context:N | Show N lines of context |
iwhite | Ignore whitespace changes |
icase | Ignore case changes |
iblank | Ignore blank lines |
horizontal | Use horizontal splits |
vertical | Use vertical splits |
followwrap | Follow ‘wrap’ setting |
indent-heuristic | Better diff output |
Inline Diff Options
:set diffopt+=inline:simple " Highlight changed regions simply
:set diffopt+=inline:char " Highlight character differences
:set diffopt+=inline:word " Highlight word differences
:set diffopt+=inline:none " Don't highlight inline changes
Managing Diff Windows
Update Diffs
:diffupdate " Recalculate differences
:diffupdate! " Recalculate and check for external changes
Neovim tries to keep diffs updated automatically, but complex changes may require manual :diffupdate.
Exit Diff Mode
:diffoff " Turn off diff mode in current window
:diffoff! " Turn off diff in all windows
:set nodiff " Alternative for current window
Practical Diff Workflows
Compare File with Saved Version
Create a command to diff the current buffer with the file on disk:
command! DiffOrig vert new | set buftype=nofile | read ++edit # | 0d_
\ | diffthis | wincmd p | diffthis
" Usage:
:DiffOrig
Make changes to a file
Edit your file without saving.
Compare with saved version
Navigate and merge
Use ]c and [c to jump between changes, do and dp to merge.
Three-Way Merge
For merge conflicts:
nvim -d base.txt mine.txt theirs.txt
Accept Mine
Accept Theirs
Manual Merge
" In 'mine.txt' window
:diffget //2 " Get from theirs (if needed)
:diffput //1 " Put to base
" In 'theirs.txt' window
:diffget //3 " Get from mine (if needed)
:diffput //1 " Put to base
" Edit base.txt manually
" Use do/dp to pull from mine or theirs
2do " Obtain from buffer 2
3do " Obtain from buffer 3
Advanced Diff Features
Diff Anchors
Force diff alignment at specific lines:
" Set marks in both files at corresponding lines
:set diffanchors='a
" Or use patterns
:set diffanchors=1/^function\ main(
" Enable anchor mode
:set diffopt+=anchor
Diff anchors help when functions or blocks have moved between files.
Custom Diff Expression
Use a custom diff program:
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
Patch Files
Apply and view patches:
:diffpatch changes.patch " Apply patch and show diff
Custom patch expression:
set patchexpr=MyPatch()
function! MyPatch()
call system("patch -o " .. v:fname_out .. " "
\ .. v:fname_in .. " < " .. v:fname_diff)
endfunction
Diff Folding
Unchanged lines are automatically folded:
zo " Open fold to see context
zc " Close fold
zr " Reduce folding (more context)
zm " More folding (less context)
Configure Diff Context
:set diffopt=context:5 " Show 5 lines of context
:set diffopt=context:0 " Show only changes
:set diffopt=context:999 " Show entire file
Example Configurations
Configure Neovim as your Git merge tool:
# In ~/.gitconfig
[merge]
tool = nvim
[mergetool "nvim"]
cmd = nvim -d $LOCAL $MERGED $REMOTE
trustExitCode = true
[mergetool]
keepBackup = false
Diff Keybindings
" Quick diff commands
nnoremap <leader>dt :diffthis<CR>
nnoremap <leader>do :diffoff<CR>
nnoremap <leader>du :diffupdate<CR>
nnoremap <leader>dg :diffget<CR>
nnoremap <leader>dp :diffput<CR>
" Navigate diffs
nnoremap <leader>dn ]c
nnoremap <leader>dN [c
" Quick 3-way merge
nnoremap <leader>d1 :diffget //2<CR>
nnoremap <leader>d2 :diffget //3<CR>
Auto-save After Diff Obtain
" Automatically save after getting changes
autocmd BufWritePost * if &diff | diffupdate | endif
Troubleshooting
Diff Not Updating
:diffupdate " Force recalculation
:set diffopt+=algorithm:histogram " Try different algorithm
Alignment Issues
:set nodiff | set diff " Reset diff mode
:windo diffoff | windo diffthis " Restart diff in all windows
:set diffopt-=indent-heuristic " Disable heuristic
:set diffopt=filler,context:1 " Reduce context
:set diffexpr= " Use internal diff (faster)
For large files, consider reducing context or using the internal diff library (leave diffexpr empty).