vim plugin to diff two directories like BeyondCompare by using diff
inspired by will133/vim-dirdiff
-
why another directory diffing plugin?
- 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)
- format the
if you like my work, check here for a list of my vim plugins, or buy me a coffee
-
install by vim-plug or any other plugin manager:
Plug 'ZSaberLv0/ZFVimDirDiff' Plug 'ZSaberLv0/ZFVimIgnore' " required only if you want to use default g:ZFDirDiffFileExclude settings Plug 'ZSaberLv0/ZFVimBackup' " required only if you want to enable auto backup
-
use
:ZFDirDiff
command to start diff:ZFDirDiff pathA pathB
if path contains spaces:
:ZFDirDiff path\ A path\ B :call zfdirdiff#DirDiff("path A", "path B")
-
use
:ZFDirDiffMark
to mark two directories to start diffOpen a file and
:ZFDirDiffMark
and 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 :ZFDirDiffMark
Or 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
m
to popup menu, pressz
to 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#DirDiff("path A", "path B")' sh ZFDirDiff.sh "path A" "path B"
-
within the diff window:
- use
DD
to update the diff result - use
o
or<cr>
to diff current file, or fold/unfold current dir - use
O
to unfold all contents under current dir,x
to fold to parent,X
to fold to root - use
cd
to make current dir as diff root dir,u
to go up for current side, andU
to go up for both side - use
DM
to mark current file, andDM
again on another file to diff these two files - use
]c
orDJ
to move to next diff,[c
orDK
to prev diff - use
do
orDH
to sync from another side to current side,dp
orDL
to sync from current side to another side - use
dd
to delete node under cursor - use
DN
to mark mutiple files, when done, usedo/DH/dp/DL/dd
to sync or delete marked files - use
p
to copy the node's path, andP
for the node's full path - use
q
to exit diff - you may also want to use ZSaberLv0/ZFVimIndentMove or easymotion/vim-easymotion to quickly move between file tree node
- use
-
ZFDirDiff_DirDiffEnter
called when enter dir diff buffer (each time for left and right window buffer)
buffer local vars:
-
t:ZFDirDiff_ownerTab
:tabpagenr()
that open the diff task -
t:ZFDirDiff_fileLeft
: abs path of left dir -
t:ZFDirDiff_fileRight
: abs path of right dir -
t:ZFDirDiff_fileLeftOrig
: original path passed from param -
t:ZFDirDiff_fileRightOrig
: original path passed from param -
t:ZFDirDiff_hasDiff
: whether has diff -
t:ZFDirDiff_data
: data return fromZF_DirDiffCore()
[ { 'level' : 'depth of tree node, 0 for top most ones', 'path' : 'path relative to fileLeft/fileRight', 'name' : 'file or dir name, empty if fileLeft and fileRight is file', 'type' : '', // T_DIR: current node is dir // T_SAME: current node is file and has no diff // T_DIFF: current node is file and has diff // T_DIR_LEFT: only left exists and it is dir // T_DIR_RIGHT: only right exists and it is dir // T_FILE_LEFT: only left exists and it is dir // T_FILE_RIGHT: only right exists and it is dir // T_CONFLICT_DIR_LEFT: left is dir and right is file // T_CONFLICT_DIR_RIGHT: left is file and right is dir 'diff' : '0/1, whether this node or children node contains diff', 'children' : [ ... ], }, ... ]
-
t:ZFDirDiff_dataUI
: list of each line for building UIthis is a plain list including folded item, you may also use
t:ZFDirDiff_dataUIVisible
for visible item list[{ 'index' : 'index in t:ZFDirDiff_dataUI', 'indexVisible' : 'index in t:ZFDirDiff_dataUIVisible, -1 when not visible', 'folded' : 'true when this item is dir and folded', 'data' : { // original data of t:ZFDirDiff_data ... }, }]
-
b:ZFDirDiff_isLeft
: true if cur buffer is left -
b:ZFDirDiff_iLineOffset
: first item's offset accorrding to header lines
-
-
ZFDirDiff_FileDiffEnter
called when enter file diff buffer (each time for left and right window buffer)
buffer local vars:
t:ZFDirDiff_ownerDiffTab
:tabpagenr()
of owner diff buffer that open this file diff task
- for core logic:
-
let g:ZFDirDiffShowSameFile = 1
: whether to show files that are same -
let g:ZFDirDiffFileExclude = ''
: file name exclude pattern, e.g.*.class,*.o
it's recommended to use
Plug 'ZSaberLv0/ZFVimIgnore'
for ignore settings, you may disable this bylet g:ZFDirDiffFileExcludeUseDefault=0
-
function! ZFDirDiffCustomFilter(path, type)
if you want to supply custom filter logic, define a function named
ZFDirDiffCustomFilter
before:ZFDirDiff
function! ZFDirDiffCustomFilter(path, type) if match(path, 'xxx') >= 0 " return 1 to ignore this item return 1 else return 0 endif endfunction
NOTE: the filter function affects all
:ZFDirDiff
after defined, you may want todelfunction! ZFDirDiffCustomFilter
after:ZFDirDiff
call -
let g:ZFDirDiffContentExclude = ''
: file content exclude pattern, e.g.log:,id:
-
let g:ZFDirDiffFileIgnoreCase = 0
: whether ignore file name case -
let g:ZFDirDiffCustomDiffArg = ''
: additional diff args passed todiff
-
let g:ZFDirDiffSortFunc = 'ZF_DirDiffSortFunc'
: sort function
-
- for builtin UI impl:
-
let g:ZFDirDiffUI_filetypeLeft = 'ZFDirDiffLeft'
:filetype
for left diff buffer -
let g:ZFDirDiffUI_filetypeRight = 'ZFDirDiffRight'
:filetype
for right diff buffer -
let g:ZFDirDiffUI_tabstop = 2
:tabstop
for diff buffer -
let g:ZFDirDiffUI_headerTextFunc = 'YourFunc'
: function name to get the header text" return a list of strings - one per line function! YourFunc() if b:ZFDirDiff_isLeft return [t:ZFDirDiff_fileLeft, ''] else return [t:ZFDirDiff_fileRight, ''] endif endfunction
-
let g:ZFDirDiffUI_confirmHintHeaderFunc = 'YourFunc'
: function name to get the header text for confirmation prompts- type:
- 'l2r' : sync left to right
- 'r2l' : sync right to left
- 'dl' : delete left
- 'dr' : delete right
- 'diff' : diff two path
" return a list of strings - one per line function! YourFunc(fileLeft, fileRight, type) return ['LEFT: ' . a:fileLeft, 'RIGHT: ' . a:fileRight] endfunction
- type:
-
let g:ZFDirDiffUI_autoBackup = 1
orlet t:ZFDirDiffUI_autoBackup = 1
: whether backup before write or delete files, requireZSaberLv0/ZFVimBackup
installed -
let g:ZFDirDiffUI_syncSameFile = 0
: whether need to sync same file, can be local to tabt:ZFDirDiffUI_syncSameFile
-
whether confirm before sync (can be local to tab
t:xxx
)let g:ZFDirDiffConfirmSyncDir = 1
let g:ZFDirDiffConfirmSyncFile = 1
let g:ZFDirDiffConfirmCopyDir = 1
let g:ZFDirDiffConfirmCopyFile = 0
let g:ZFDirDiffConfirmRemoveDir = 1
let g:ZFDirDiffConfirmRemoveFile = 1
-
keymaps
let g:ZFDirDiffKeymap_update = ['DD']
let g:ZFDirDiffKeymap_open = ['<cr>', 'o']
let g:ZFDirDiffKeymap_foldOpenAll = ['O']
let g:ZFDirDiffKeymap_foldClose = ['x']
let g:ZFDirDiffKeymap_foldCloseAll = ['X']
let g:ZFDirDiffKeymap_goParent = ['U']
let g:ZFDirDiffKeymap_diffThisDir = ['cd']
let g:ZFDirDiffKeymap_diffParentDir = ['u']
let g:ZFDirDiffKeymap_markToDiff = ['DM']
let g:ZFDirDiffKeymap_markToSync = ['DN']
let g:ZFDirDiffKeymap_quit = ['q']
let g:ZFDirDiffKeymap_quitFileDiff = ['q']
let g:ZFDirDiffKeymap_nextDiff = [']c', 'DJ']
let g:ZFDirDiffKeymap_prevDiff = ['[c', 'DK']
let g:ZFDirDiffKeymap_syncToHere = ['do', 'DH']
let g:ZFDirDiffKeymap_syncToThere = ['dp', 'DL']
let g:ZFDirDiffKeymap_deleteFile = ['dd']
let g:ZFDirDiffKeymap_getPath = ['p']
let g:ZFDirDiffKeymap_getFullPath = ['P']
-
highlight
highlight link ZFDirDiffHL_Title Title highlight link ZFDirDiffHL_Dir Directory highlight link ZFDirDiffHL_DirContainDiff Directory highlight link ZFDirDiffHL_Same Folded highlight link ZFDirDiffHL_Diff DiffText highlight link ZFDirDiffHL_DirOnlyHere DiffAdd highlight link ZFDirDiffHL_DirOnlyThere Normal highlight link ZFDirDiffHL_FileOnlyHere DiffAdd highlight link ZFDirDiffHL_FileOnlyThere Normal highlight link ZFDirDiffHL_ConflictDir ErrorMsg highlight link ZFDirDiffHL_ConflictFile WarningMsg highlight link ZFDirDiffHL_MarkToDiff Cursor
-
-
Q: how to use under special shell config
A: when used under special shell config, especially
sh
under Windows, here's a list of configs you should concern:-
let g:ZFDirDiffLangString = 'LANG= '
by default, this value is set to
set LAND= &&
on Windows to suitcmd.exe
you may want to set this to suit your shell, e.g.
let g:ZFDirDiffLangString = 'LANG= '
-
ZF_DirDiffTempname()
by default, this function use vim's builtin
tempname()
however, it may result to
C:\xxx\tmp
on Windows, which can not be read bybash
, you may want to supply your own function to suit your shell, e.g.function! ZF_DirDiffTempname() return '/xxx/tmp' endfunction
-
ZF_DirDiffShellEnv_pathFormat(path)
by default, this function use
fnamemodify(path, ':.')
to make the path relative togetcwd()
if your shell can't read with it, you may supply your own, e.g.
function! ZF_DirDiffShellEnv_pathFormat(path) return substitute(system('cygpath -m "' . a:path . '"'), '[\r\n]', '', 'g') endfunction
-