ZFVimDirDiff
ZFVimDirDiff copied to clipboard
directory diff plugin with split treeview, similar to BeyondCompare, fully async and low dependency
Intro
vim plugin to diff two directories like BeyondCompare by using diff
inspired by will133/vim-dirdiff
-
why another directory diff plugin?
- fully async and queued, works well with tons of files, even for vim 7.3! (by ZFVimJob)
- works well on Windows without
shordiffenv - format the diff result as vertical split file tree view, which should be more human-readable
- more friendly file sync operation using the same mappings as builtin
vimdiff - automatically backup before destructive actions (by ZFVimBackup)
- better file or directory exclude logic (by ZFVimIgnore)

if you like my work, check here for a list of my vim plugins, or buy me a coffee
How to use
-
requirement
- vim 8.0 or neovim : recommend, fully async
- vim 7.3 or above : all features work as expected, with some lag due to lack of
job
-
install by vim-plug or any other plugin manager:
Plug 'ZSaberLv0/ZFVimDirDiff' Plug 'ZSaberLv0/ZFVimJob' " required Plug 'ZSaberLv0/ZFVimIgnore' " optional, but recommended for auto ignore setup Plug 'ZSaberLv0/ZFVimBackup' " optional, but recommended for auto backup -
use
:ZFDirDiffcommand to start diff:ZFDirDiff pathA pathBif path contains spaces:
:ZFDirDiff path\ A path\ B :call ZFDirDiff("path A", "path B") -
use
:ZFDirDiffMarkto mark two directories to start diffOpen a file and
:ZFDirDiffMarkand the containing directory will be stored as a diff candidate. Then repeat with another file and you'll be asked to diff the two.:edit pathA/file.vim :ZFDirDiffMark :edit pathB/file.vim :ZFDirDiffMarkOr integrate with your file manager. For vim-dirvish, add ~/.vim/ftplugin/dirvish.vim:
nnoremap <buffer> X :<C-u>ZFDirDiffMark <C-r><C-l><CR>Or for netrw, add ~/.vim/ftplugin/netrw.vim:
nnoremap <buffer> X :<C-u>ZFDirDiffMark <C-r>=b:netrw_curdir<CR>/<C-r><C-l><CR>Then X on two directories.
-
you can also start diff from scrooloose/nerdtree: inside nerdtree window, press
mto popup menu, presszto choosemark to diff, and mark another node again to start diff -
you may also use it as command line diff tool
vim -c 'call ZFDirDiff("path A", "path B")' sh ZFDirDiff.sh "path A" "path B" -
within the diff window:
- use
DDto update the diff result under cursor - use
oor<cr>to diff current file, or fold/unfold current dir - use
Oto unfold all contents under current dir,xto fold to parent,Xto fold to root - use
cdto make current dir as diff root dir,uto go up for current side, andUto go up for both side - use
DMto mark current file, andDMagain on another file to diff these two files - use
]corDJto move to next diff,[corDKto prev diff, useDj/Dkto move to next / prev diff file - use
doorDHto sync from another side to current side,dporDLto sync from current side to another side - use
ato add new file or dir - use
ddto delete node under cursor - use
DNto mark mutiple files, when done, usedo/DH/dp/DL/ddto sync or delete marked files - use
pto copy the node's path, andPfor the node's full path - use
qto exit diff - you may also want to use ZSaberLv0/ZFVimIndentMove or easymotion/vim-easymotion to quickly move between file tree node
- use
-
within the file diff window:
- it's vim's builtin diff, see
:h difffor more info - use
qto quick file diff and back to owner diff window
- it's vim's builtin diff, see
Configs
this plugin should work well without any extra config
for experienced user, here's some configs you may interest
Diff logic
-
let g:ZFDirDiff_autoBackup = 1: whether perform auto backup, see https://github.com/ZSaberLv0/ZFVimBackup -
let g:ZFIgnoreOption_ZFDirDiff = {...}: ignore options, see https://github.com/ZSaberLv0/ZFVimIgnorelet g:ZFIgnoreOption_ZFDirDiff = { \ 'bin' : 0, \ 'media' : 0, \ 'ZFDirDiff' : 1, \ }
Keymap (inside diff window)
let g:ZFDirDiffKeymap_update = []: update entire diff windowlet g:ZFDirDiffKeymap_updateParent = ['DD']: update diff under cursorlet g:ZFDirDiffKeymap_open = ['<cr>', 'o']: toggle dir open or open file difflet g:ZFDirDiffKeymap_foldOpenAll = []: open all node under cursor, including same fileslet g:ZFDirDiffKeymap_foldOpenAllDiff = ['O']: open all diff node under cursorlet g:ZFDirDiffKeymap_foldClose = ['x']: close nodelet g:ZFDirDiffKeymap_foldCloseAll = ['X']: close all nodelet g:ZFDirDiffKeymap_goParent = ['U']: make both left and right diff window go to parent dirlet g:ZFDirDiffKeymap_diffThisDir = ['cd']: change current side's root to node under cursorlet g:ZFDirDiffKeymap_diffParentDir = ['u']: change current side's root to parentlet g:ZFDirDiffKeymap_markToDiff = ['DM']: mark node under cursor, mark again to diff with two marked nodelet g:ZFDirDiffKeymap_markToSync = ['DN']: mark one or more nodes, to sync mutiple nodes at oncelet g:ZFDirDiffKeymap_quit = ['q']: quit difflet g:ZFDirDiffKeymap_diffNext = [']c', 'DJ']: jump to next visible difflet g:ZFDirDiffKeymap_diffPrev = ['[c', 'DK']: jump to prev visible difflet g:ZFDirDiffKeymap_diffNextFile = ['Dj']: jump to next diff file, auto open closed dirlet g:ZFDirDiffKeymap_diffPrevFile = ['Dk']: jump to prev diff file, auto open closed dirlet g:ZFDirDiffKeymap_syncToHere = ['do', 'DH']: sync nodes from there to herelet g:ZFDirDiffKeymap_syncToThere = ['dp', 'DL']: sync nodes from here to therelet g:ZFDirDiffKeymap_add = ['a']: add new node, end with/to add dirlet g:ZFDirDiffKeymap_delete = ['dd']: delete selected nodeslet g:ZFDirDiffKeymap_getPath = ['p']: get relative path of node under cursorlet g:ZFDirDiffKeymap_getFullPath = ['P']: get absolute path of node under cursor
Keymap (inside file diff window)
let g:ZFDirDiffKeymap_quitFileDiff = ['q']: quit file diff, and go back to its owner diff window
UI spec
let g:ZFDirDiffUIChar_dir_prefix_closed = '+ 'let g:ZFDirDiffUIChar_dir_prefix_opened = '~ 'let g:ZFDirDiffUIChar_dir_postfix = '/'let g:ZFDirDiffUIChar_file_prefix = ' 'let g:ZFDirDiffUIChar_file_postfix = ''let g:ZFDirDiffUI_tabstop = 2let g:ZFDirDiffUI_autoOpenSingleChildDir = 1let g:ZFDirDiffUI_showSameDir = 1let g:ZFDirDiffUI_showSameFile = 1
Highlight
highlight default link ZFDirDiffHL_Header Title
highlight default link ZFDirDiffHL_Tail Title
highlight default link ZFDirDiffHL_Checking SpecialKey
highlight default link ZFDirDiffHL_DirSame Folded
highlight default link ZFDirDiffHL_DirDiff DiffAdd
highlight default link ZFDirDiffHL_FileSame Folded
highlight default link ZFDirDiffHL_FileDiff DiffText
highlight default link ZFDirDiffHL_DirOnlyHere DiffAdd
highlight default link ZFDirDiffHL_FileOnlyHere DiffAdd
highlight default link ZFDirDiffHL_ConflictDirHere ErrorMsg
highlight default link ZFDirDiffHL_ConflictDirThere WarningMsg
highlight default link ZFDirDiffHL_MarkToDiff Cursor
highlight default link ZFDirDiffHL_MarkToSync Cursor
FAQ
-
Q: screen keeps blink when diff updating in background
A: unfortunately, I have no idea for how to solve this issue, mainly because of
matchadd()must inside proper window, causing frequent window switching