1 Commits

Author SHA1 Message Date
Tim Pope
ef87434b87 Provide :Git difftool and :Git mergetool
References https://github.com/tpope/vim-fugitive/issues/132
2020-01-05 16:36:11 -05:00
4 changed files with 84 additions and 109 deletions

View File

@@ -33,9 +33,6 @@ Use `:Ggrep` to search the work tree (or any arbitrary commit) with
list. Give them a range (e.g., using visual mode and `:'<,'>Gclog`) to
iterate over every change to that portion of the current file.
`:Git mergetool` loads conflicts into the quickfix list. `:Git difftool` does
the same for any arbitrary set of changes.
`:Gread` is a variant of `git checkout -- filename` that operates on the
buffer rather than the filename. This means you can use `u` to undo it
and you never get any warnings about the file changing outside Vim.
@@ -92,8 +89,9 @@ request the password via a GUI. Fugitive will configure this for you
automatically if you have `ssh-askpass` or `git-gui` installed; otherwise it's
your responsibility to set this up.
If you absolutely must type in your password by hand, sidestep Fugitive and
use `:terminal git push`.
As an absolute last resort, you can invoke `:Git --paginate push`. Fugitive
recognizes the pagination request and fires up a `:terminal`, which allows for
interactive password entry.
[credentials caching]: https://help.github.com/en/articles/caching-your-github-password-in-git

View File

@@ -42,7 +42,7 @@ function! s:Uniq(list) abort
endfunction
function! s:winshell() abort
return has('win32') && &shellcmdflag !~# '^-'
return has('win32') && &shellcmdflag =~# '^/\|^-Command$'
endfunction
function! s:shellesc(arg) abort
@@ -414,24 +414,17 @@ function! fugitive#PrepareDirEnvArgv(...) abort
return [dir, env, cmd]
endfunction
function! s:BuildEnvPrefix(env) abort
let pre = ''
let env = items(a:env)
if empty(env)
return ''
elseif &shellcmdflag =~# '-Command'
return join(map(env, '"$Env:" . v:val[0] . " = ''" . substitute(v:val[1], "''", "''''", "g") . "''; "'), '')
elseif s:winshell()
return join(map(env, '"set " . substitute(join(v:val, "="), "[&|<>^]", "^^^&", "g") . "& "'), '')
else
return 'env ' . s:shellesc(map(env, 'join(v:val, "=")')) . ' '
endif
endfunction
function! s:BuildShell(dir, env, args) abort
let cmd = copy(a:args)
let tree = s:Tree(a:dir)
let pre = s:BuildEnvPrefix(a:env)
let pre = ''
for [var, val] in items(a:env)
if s:winshell()
let pre .= 'set ' . var . '=' . s:shellesc(val) . '& '
else
let pre = (len(pre) ? pre : 'env ') . var . '=' . s:shellesc(val) . ' '
endif
endfor
if empty(tree) || index(cmd, '--') == len(cmd) - 1
call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
elseif fugitive#GitVersion(1, 8, 5)
@@ -1267,16 +1260,17 @@ function! fugitive#setfperm(url, perm) abort
endfunction
function! s:TempCmd(out, cmd) abort
let prefix = ''
try
let cmd = (type(a:cmd) == type([]) ? fugitive#Prepare(a:cmd) : a:cmd)
let redir = ' > ' . a:out
if (s:winshell() || &shellcmdflag ==# '-Command') && !has('nvim')
if s:winshell()
let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
return s:SystemError('cmd /c "' . s:gsub(cmd, '[<>%]', cmd_escape_char . '&') . redir . '"')
return s:SystemError('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"')
elseif &shell =~# 'fish'
return s:SystemError(' begin;' . cmd . redir . ';end ')
return s:SystemError(' begin;' . prefix . cmd . redir . ';end ')
else
return s:SystemError(' (' . cmd . redir . ') ')
return s:SystemError(' (' . prefix . cmd . redir . ') ')
endif
endtry
endfunction
@@ -1561,13 +1555,28 @@ function! s:ReplaceCmd(cmd) abort
if exec_error
call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
endif
silent exe 'keepalt $read ++edit' s:fnameescape(temp)
silent keepjumps 1delete _
call delete(temp)
let temp = s:Resolve(temp)
let fn = expand('%:p')
silent exe 'keepalt file '.temp
let modelines = &modelines
try
set modelines=0
silent keepjumps noautocmd edit!
finally
let &modelines = modelines
try
silent exe 'keepalt file '.s:fnameescape(fn)
catch /^Vim\%((\a\+)\)\=:E302:/
endtry
call delete(temp)
if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
silent! execute 'bwipeout '.bufnr('$')
endif
endtry
endfunction
function! s:QueryLog(refspec) abort
let lines = s:LinesError(['log', '-n', '256', '--pretty=format:%h%x09%s', a:refspec, '--'])[0]
let lines = s:LinesError(['log', '-n', '256', '--format=%h%x09%s', a:refspec, '--'])[0]
call map(lines, 'split(v:val, "\t")')
call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
return lines
@@ -1826,8 +1835,7 @@ function! fugitive#BufReadStatus() abort
if push !=# pull
call s:AddHeader('Push', push)
endif
let tree = s:Tree()
if empty(tree)
if empty(s:Tree())
call s:AddHeader('Bare', 'yes')
endif
call s:AddSection('Rebasing ' . rebasing_head, rebasing)
@@ -1845,8 +1853,7 @@ function! fugitive#BufReadStatus() abort
if &bufhidden ==# ''
setlocal bufhidden=delete
endif
let b:dispatch = '-compiler=git -dir=' . s:fnameescape(FugitiveVimPath(len(tree) ? tree : s:Dir())) .
\ ' ' . s:UserCommand() . ' ' . s:shellesc(s:AskPassArgs(s:Dir())) . ' fetch --all'
let b:dispatch = ':Gfetch --all'
call fugitive#MapJumps()
call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
@@ -1909,7 +1916,7 @@ function! fugitive#BufReadStatus() abort
endfor
let b:fugitive_reltime = reltime()
return 'silent ' . s:DoAutocmd('User FugitiveIndex')
return ''
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
@@ -2012,7 +2019,7 @@ function! fugitive#BufReadCmd(...) abort
setlocal endofline
try
silent exe s:DoAutocmd('BufReadPre')
silent doautocmd BufReadPre
if b:fugitive_type ==# 'tree'
let b:fugitive_display_format = b:fugitive_display_format % 2
if b:fugitive_display_format
@@ -2072,16 +2079,8 @@ function! fugitive#BufReadCmd(...) abort
endtry
setlocal modifiable
let browsex = maparg('<Plug>NetrwBrowseX', 'n')
let remote_check = '\Cnetrw#CheckIfRemote(\%(netrw#GX()\)\=)'
if browsex =~# remote_check
exe 'nnoremap <silent> <buffer> <Plug>NetrwBrowseX' substitute(browsex, remote_check, '0', 'g')
endif
return 'silent ' . s:DoAutocmd('BufReadPost') .
\ (modifiable ? '' : '|setl nomodifiable') . '|silent ' .
\ s:DoAutocmd('User Fugitive' . substitute(b:fugitive_type, '^\l', '\u&', ''))
\ (modifiable ? '' : '|setl nomodifiable')
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
@@ -2172,26 +2171,22 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
try
exe s:DirCheck(dir)
let opts = s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])
if type(opts) == type('')
return 'exe ' . string(opts) . after
let result = s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])
if type(result) == type('')
return 'exe ' . string(result) . after
endif
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
else
let opts = {}
endif
if a:bang || args[0] =~# '^-p$\|^--paginate$\|diff\%(tool\)\@!\|log\|^show$' ||
if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' ||
\ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
\ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
if args[0] =~# '^-p$\|^--paginate$'
call remove(args, 0)
endif
return s:OpenExec((a:line2 > 0 ? a:line2 : '') . (a:line2 ? 'split' : 'edit'), a:mods, args, dir) . after
endif
if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') ||
\ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive')
\ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive') ||
\ index(['--paginate', '-p'], args[0]) >= 0
let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : ''
if has('nvim')
@@ -2202,14 +2197,13 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
return 'exe ' . string(mods . 'terminal ' . (a:line2 ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . assign . after
endif
endif
let env = get(opts, 'env', {})
if has('gui_running') && !has('win32')
call insert(args, '--no-pager')
endif
if has('nvim')
let env.GIT_TERMINAL_PROMPT = '0'
let pre = ''
if has('nvim') && executable('env')
let pre .= 'env GIT_TERMINAL_PROMPT=0 '
endif
let pre = s:BuildEnvPrefix(env)
return 'exe ' . string('noautocmd !' . escape(pre . s:UserCommand(dir, args), '!#%')) .
\ '|call fugitive#ReloadStatus(' . string(dir) . ', 1)' .
\ after
@@ -3383,7 +3377,7 @@ function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
let outfile = tempname()
try
if s:winshell() || &shellcmdflag ==# '-Command'
if s:winshell()
let command = 'set GIT_EDITOR=false& '
else
let command = 'env GIT_EDITOR=false '
@@ -3538,7 +3532,7 @@ function! s:RebaseSequenceAborter() abort
\ 'echo exec false | cat - "$1" > "$1.fugitive"',
\ 'mv "$1.fugitive" "$1"'],
\ temp)
let s:rebase_sequence_aborter = FugitiveGitPath(temp)
let s:rebase_sequence_aborter = temp
endif
return s:rebase_sequence_aborter
endfunction
@@ -3671,7 +3665,13 @@ function! s:MergeRebase(cmd, bang, mods, args, ...) abort
\ . "%+EXUNG \u0110\u1ed8T %.%#,"
\ . "%+E\u51b2\u7a81 %.%#,"
\ . 'U%\t%f'
let cmd = s:UserCommand(dir, argv)
if a:cmd =~# '^merge' && empty(args) &&
\ (had_merge_msg || isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
\ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
let cmd = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
else
let cmd = s:UserCommand(dir, argv)
endif
if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
let old_sequence_editor = $GIT_SEQUENCE_EDITOR
let $GIT_SEQUENCE_EDITOR = 'true'
@@ -3761,13 +3761,6 @@ function! s:RebaseClean(file) abort
endfunction
function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort
let dir = s:Dir()
if empty(a:args) && (
\ filereadable(fugitive#Find('.git/MERGE_MSG', dir)) ||
\ isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
\ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
return 'echohl WarningMsg|echo ":Git merge for loading conflicts is deprecated in favor of :Git mergetool"|echohl NONE|silent Git' . (a:bang ? '!' : '') . ' mergetool'
endif
return s:MergeRebase('merge', a:bang, a:mods, a:args)
endfunction
@@ -3787,7 +3780,7 @@ endfunction
augroup fugitive_merge
autocmd!
autocmd VimLeavePre,BufDelete git-rebase-todo
\ if type(getbufvar(+expand('<abuf>'), 'fugitive_rebase_shas')) == type({}) && getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
\ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
\ call s:RebaseClean(expand('<afile>')) |
\ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
\ let s:rebase_continue = [FugitiveGitDir(+expand('<abuf>')), 1] |
@@ -3859,9 +3852,6 @@ function! s:ToolParse(state, line) abort
let offsets = split(matchstr(a:line, '^@\+ \zs[-+0-9, ]\+\ze @'), ' ')
return s:ToolItems(a:state, a:state.from, a:state.to, offsets, matchstr(a:line, ' @@\+ \zs.*'))
endif
elseif a:line =~# '^\* Unmerged path .'
let file = a:line[16:-1]
return s:ToolItems(a:state, file, file, [], '')
elseif a:line =~# '^[A-Z]\d*\t.\|^:.*\t.'
" --raw, --name-status
let [status; files] = split(a:line, "\t")
@@ -3900,7 +3890,7 @@ function! s:ToolStream(dir, line1, line2, range, bang, mods, args, state, title)
endif
let arg = argv[i]
if arg =~# '^-t$\|^--tool=\|^--tool-help$\|^--help$'
return {}
return -1
elseif arg =~# '^-y$\|^--no-prompt$'
let prompt = 0
call remove(argv, i)
@@ -4094,7 +4084,11 @@ function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
call add(cmd, '--column')
endif
let tree = s:Tree(dir)
let args = a:args
if type(a:args) == type([])
let [args, after] = [a:args, '']
else
let [args, after] = s:SplitExpandChain(a:args, tree)
endif
let prefix = FugitiveVimPath(s:HasOpt(args, '--cached') || empty(tree) ? 'fugitive://' . dir . '//0/' : tree . '/')
let name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
@@ -4117,9 +4111,9 @@ function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
redraw
endif
if !a:bang && !empty(list)
return (listnr < 0 ? 'c' : 'l').'first'
return (listnr < 0 ? 'c' : 'l').'first' . after
else
return ''
return after[1:-1]
endif
endfunction
@@ -4414,7 +4408,13 @@ function! s:OpenExec(cmd, mods, args, ...) abort
let dir = a:0 ? s:Dir(a:1) : s:Dir()
let temp = tempname()
let columns = get(g:, 'fugitive_columns', 80)
let env = s:BuildEnvPrefix({'COLUMNS': columns})
if columns <= 0
let env = ''
elseif s:winshell()
let env = 'set COLUMNS=' . columns . '& '
else
let env = 'env COLUMNS=' . columns . ' '
endif
silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
\ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
redraw!
@@ -5710,12 +5710,7 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, args) abo
endif
let i = 0
while commit =~# '^ref: ' && i < 10
let ref_file = cdir . '/' . commit[5:-1]
if getfsize(ref_file) > 0
let commit = readfile(ref_file, '', 1)[0]
else
let commit = fugitive#RevParse(commit[5:-1], dir)
endif
let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
let i -= 1
endwhile
endif
@@ -5937,7 +5932,6 @@ function! fugitive#MapJumps(...) abort
nnoremap <buffer> cm<Space> :Git merge<Space>
nnoremap <buffer> cm<CR> :Git merge<CR>
nnoremap <buffer> cmt :Git mergetool
nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
nnoremap <buffer> cz<Space> :Git stash<Space>

View File

@@ -20,9 +20,10 @@ that are part of Git repositories).
:G {args} but chdir to the repository tree first. For some
subcommands, a Fugitive command is called instead.
*:Git!*
:Git! {args} Like |:Git|, but capture the output into a temp file,
:Git --paginate {args} and |:split| that temp file. Use :0Git to
:Git -p {args} |:edit| the temp file instead. A temp file is always
:Git --no-pager {args} and |:split| that temp file. Use :0Git to
:Git -P {args} |:edit| the temp file instead. A temp file is always
used for diff and log commands.
*:Gstatus*
@@ -110,7 +111,7 @@ that are part of Git repositories).
change unless [!] is given.
:Git difftool -y [args] Invoke `git diff [args]`, open each changed file in a
new tab, and invoke `:Gdiffsplit!` against the
new tab, and invoke `:Gdiffsplit` against the
appropriate commit.
*:Git-mergetool*
@@ -207,7 +208,7 @@ that are part of Git repositories).
focus on the current window. During a merge conflict,
this is a three-way diff against the "ours" and
"theirs" ancestors. Additional d2o and d3o maps are
provided to obtain the hunk from the "ours" or
provided to to obtain the hunk from the "ours" or
"theirs" ancestor, respectively.
:Gdiffsplit! {object} Like |:Gdiffsplit|, but retain focus on the current

View File

@@ -1,6 +1,6 @@
" fugitive.vim - A Git wrapper so awesome, it should be illegal
" Maintainer: Tim Pope <http://tpo.pe/>
" Version: 3.2
" Version: 3.1
" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
if exists('g:loaded_fugitive')
@@ -66,8 +66,8 @@ function! FugitivePath(...) abort
endfunction
" FugitiveParse() takes a fugitive:// URL and returns a 2 element list
" containing an object name ("commit:file") and the Git dir. It's effectively
" the inverse of FugitiveFind().
" containing the Git dir and an object name ("commit:file"). It's effectively
" then inverse of FugitiveFind().
function! FugitiveParse(...) abort
let path = s:Slash(a:0 ? a:1 : @%)
if path !~# '^fugitive:'
@@ -103,24 +103,6 @@ function! FugitiveConfig(...) abort
endif
endfunction
" Retrieve a Git configuration value. An optional second argument provides
" the Git dir as with FugitiveFind(). Pass a blank string to limit to the
" global config.
function! FugitiveConfigGet(name, ...) abort
return call('FugitiveConfig', [a:name] + a:000)
endfunction
" Like FugitiveConfigGet(), but return a list of all values.
function! FugitiveConfigGetAll(name, ...) abort
if a:0 && type(a:1) ==# type({})
let config = a:1
else
let config = fugitive#Config(FugitiveGitDir(a:0 ? a:1 : -1))
endif
let name = substitute(a:name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
return copy(get(config, name, []))
endfunction
function! FugitiveRemoteUrl(...) abort
return fugitive#RemoteUrl(a:0 ? a:1 : '', FugitiveGitDir(a:0 > 1 ? a:2 : -1))
endfunction