From cd78a08543c40cd95cf6440b9e434032a3fe4677 Mon Sep 17 00:00:00 2001 From: Tim Pope Date: Sun, 7 Jul 2019 10:34:04 -0400 Subject: [PATCH] Retool diffing Support mods, rename to match the built-in :diffsplit, and (perhaps controversially) move three-way diffing to the bang variant, so that the default behavior always focuses the other window. Closes https://github.com/tpope/vim-fugitive/pull/715 --- autoload/fugitive.vim | 77 +++++++++++++++++++++++++++---------------- doc/fugitive.txt | 46 ++++++++++++++------------ 2 files changed, 73 insertions(+), 50 deletions(-) diff --git a/autoload/fugitive.vim b/autoload/fugitive.vim index 9e77eca..7cbc21e 100644 --- a/autoload/fugitive.vim +++ b/autoload/fugitive.vim @@ -76,11 +76,12 @@ function! s:DirCheck(...) abort endfunction function! s:Mods(mods, ...) abort - let mods = a:mods ==# '' || empty(a:mods) ? '' : a:mods . ' ' - if a:0 && mods !~# 'aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab' + let mods = substitute(a:mods, '\C', '', '') + let mods = mods =~# '\S$' ? a:mods . ' ' : a:mods + if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>' let mods = a:1 . ' ' . mods endif - return mods + return substitute(mods, '\s\+', ' ', 'g') endfunction function! s:Slash(path) abort @@ -1710,12 +1711,12 @@ function! fugitive#BufReadStatus() abort exe 'xnoremap ' nowait "= :execute StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)" exe 'xnoremap ' nowait "< :execute StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)" exe 'xnoremap ' nowait "> :execute StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)" - nnoremap D :execute StageDiff('Gdiff') - nnoremap dd :execute StageDiff('Gdiff') - nnoremap dh :execute StageDiff('Gsdiff') - nnoremap ds :execute StageDiff('Gsdiff') + nnoremap D :execute StageDiff('Gdiffsplit') + nnoremap dd :execute StageDiff('Gdiffsplit') + nnoremap dh :execute StageDiff('Ghdiffsplit') + nnoremap ds :execute StageDiff('Ghdiffsplit') nnoremap dp :execute StageDiffEdit() - nnoremap dv :execute StageDiff('Gvdiff') + nnoremap dv :execute StageDiff('Gvdiffsplit') nnoremap J :execute StageNext(v:count1) nnoremap K :execute StagePrevious(v:count1) nnoremap P :execute StagePatch(line('.'),line('.')+v:count1-1) @@ -3739,9 +3740,9 @@ endfunction " Section: :Gdiff -call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gdiff :execute s:Diff('',0,)") -call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gvdiff :execute s:Diff('keepalt vert ',0,)") -call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gsdiff :execute s:Diff('keepalt ',0,)") +call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gdiffsplit :execute s:Diff(1, 0, '', )") +call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gvdiffsplit :execute s:Diff(0, 0, 'vertical ', )") +call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Ghdiffsplit :execute s:Diff(0, 0, '', )") augroup fugitive_diff autocmd! @@ -3768,13 +3769,13 @@ endfunction function! s:diff_modifier(count) abort let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+') if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical' - return 'keepalt ' + return '' elseif &diffopt =~# 'vertical' - return 'keepalt vert ' + return 'vertical ' elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc)) - return 'keepalt ' + return '' else - return 'keepalt vert ' + return 'vertical ' endif endfunction @@ -3855,26 +3856,37 @@ function! s:CompareAge(mine, theirs) abort return my_time < their_time ? -1 : my_time != their_time endfunction -function! s:Diff(vert,keepfocus,...) abort +function! s:IsConflicted() abort + return !empty(s:TreeChomp('ls-files', '--unmerged', '--', expand('%:p'))) +endfunction + +function! s:Diff(autodir, keepfocus, mods, ...) abort + if exists(':DiffGitCached') && !a:0 + return s:Mods(a:mods) . 'DiffGitCached' + endif let args = copy(a:000) let post = '' if get(args, 0) =~# '^+' let post = remove(args, 0)[1:-1] endif - let vert = empty(a:vert) ? s:diff_modifier(2) : a:vert let commit = s:DirCommitFile(@%)[1] + if a:mods =~# '\' + let mods = substitute(a:mods, '\', '', 'g') + tab split + else + let mods = 'keepalt ' . a:mods + endif let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p' - if exists(':DiffGitCached') - return 'DiffGitCached' - elseif (empty(args) || args[0] ==# ':') && commit =~# '^[0-1]\=$' && !empty(s:TreeChomp('ls-files', '--unmerged', '--', expand('%:p'))) - let vert = empty(a:vert) ? s:diff_modifier(3) : a:vert + if (empty(args) || args[0] ==# ':') && commit =~# '^[0-1]\=$' && a:keepfocus && s:IsConflicted() + let mods = (a:autodir ? s:diff_modifier(3) : '') . s:Mods(mods, 'leftabove') let nr = bufnr('') - execute 'leftabove '.vert.'split' s:fnameescape(s:Generate(s:Relative(':2:'))) + execute mods 'split' s:fnameescape(s:Generate(s:Relative(':2:'))) execute 'nnoremap dp :diffput '.nr.'diffupdate' let nr2 = bufnr('') call s:diffthis() exe back - execute 'rightbelow '.vert.'split' s:fnameescape(s:Generate(s:Relative(':3:'))) + let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '') + execute mods 'split' s:fnameescape(s:Generate(s:Relative(':3:'))) execute 'nnoremap dp :diffput '.nr.'diffupdate' let nr3 = bufnr('') call s:diffthis() @@ -3903,8 +3915,13 @@ function! s:Diff(vert,keepfocus,...) abort if file !~# ':' && file !~# '^/' && s:TreeChomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$' let file = file.s:Relative(':') endif + elseif len(commit) + let file = s:Relative() + elseif s:IsConflicted() + let file = s:Relative(':1:') + let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post else - let file = empty(commit) ? s:Relative(':0:') : s:Relative() + let file = s:Relative(':0:') endif try let spec = s:Generate(file) @@ -3914,10 +3931,12 @@ function! s:Diff(vert,keepfocus,...) abort endif let w:fugitive_diff_restore = restore if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0 - execute 'rightbelow '.vert.'diffsplit '.s:fnameescape(spec) + let mods = s:Mods(mods, 'rightbelow') else - execute 'leftabove '.vert.'diffsplit '.s:fnameescape(spec) + let mods = s:Mods(mods, 'leftabove') endif + let mods = (a:autodir ? s:diff_modifier(2) : '') . mods + execute mods 'diffsplit' s:fnameescape(spec) let &l:readonly = &l:readonly redraw let w:fugitive_diff_restore = restore @@ -4789,18 +4808,18 @@ function! s:cfile() abort let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)') let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)') - let dcmd = 'Gdiff! +'.offset + let dcmd = 'Gdiffsplit! +'.offset elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)' let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)') let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)') - let dcmd = 'Gdiff!' + let dcmd = 'Gdiffsplit!' elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)' let line = getline(line('.')-1) let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)') let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)') - let dcmd = 'Gdiff!' + let dcmd = 'Gdiffsplit!' elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$' let ref = getline('.') diff --git a/doc/fugitive.txt b/doc/fugitive.txt index 23a85b3..79ae24d 100644 --- a/doc/fugitive.txt +++ b/doc/fugitive.txt @@ -143,25 +143,28 @@ that are part of Git repositories). :Gwq! [path] Like |:Gwrite|! followed by |:quit|! if the write succeeded. - *fugitive-:Gdiff* -:Gdiff [object] Perform a |vimdiff| against the given file, or if a + *fugitive-:Gdiffsplit* +:Gdiffsplit [object] Perform a |vimdiff| against the given file, or if a commit is given, the current file in that commit. - With no argument, the version in the index is used - (which means a three-way diff during a merge conflict, - making it a git-mergetool alternative). The newer of - the two files is placed to the right or bottom, - depending on 'diffopt', and the width of the window - relative to 'textwidth'. Use |do| and |dp| and write - to the index file to simulate "git add --patch". For - the three-way diff, there is also d2o and d3o pulling - the hunk to the middle from the left or the right - window, respectively. + With no argument, the version in the index or work + tree is used. The newer of the two files is placed to + the right or bottom, depending on 'diffopt' and the + width of the window relative to 'textwidth'. Use + Vim's |do| and |dp| to stage and unstage changes. - *fugitive-:Gsdiff* -:Gsdiff [object] Like |:Gdiff|, but always split horizontally. + *fugitive-:Gdiffsplit!* +:Gdiffsplit! [object] During a merge conflict, do a three-way diff against + both ancestors. Additional d2o and d3o maps are + provided to to obtain the hunk from the "ours" or + "theirs" ancestor, respectively. If the file is not + conflicted, behaves the same as if no bang was given, + but keeps focus in the original window. - *fugitive-:Gvdiff* -:Gvdiff [object] Like |:Gdiff|, but always split vertically. + *fugitive-:Gvdiffsplit* +:Gvdiffsplit [object] Like |:Gdiffsplit|, but always split vertically. + + *fugitive-:Ghdiffsplit* *fugitive-:Gsdiff* +:Ghdiffsplit [object] Like |:Gdiffsplit|, but always split horizontally. *fugitive-:Gmove* :Gmove {destination} Wrapper around git-mv that renames the buffer @@ -276,13 +279,14 @@ i On untracked files, call |:Git| add --intent-to-add. automatically. *fugitive_dd* -dd Perform a |:Gdiff| on the file under the cursor. - - *fugitive_ds* -ds Perform a |:Gsdiff| on the file under the cursor. +dd Perform a |:Gdiffsplit| on the file under the cursor. *fugitive_dv* -dv Perform a |:Gvdiff| on the file under the cursor. +dv Perform a |:Gvdiffsplit| on the file under the cursor. + + *fugitive_ds* *fugitive_dh* +ds Perform a |:Ghdiffsplit| on the file under the cursor. +dh *fugitive_dp* dp Invoke |:Git!| diff on the file under the cursor.