28 Commits

Author SHA1 Message Date
Tim Pope
21ed533001 fugitive.vim 3.2
* Provide :Git mergetool to load conflicts into quickfix list.
* Provide :Git difftool to load arbitrary changesets into quickfix list.
* Call FugitiveBlob/FugitiveCommit/etc autocommands on Fugitive buffer load.
* :Git --paginate display results in temp buffer.
* :Git --no-pager no longer displays results in temp buffer.
* Accomodate loading Fugitive buffers in popup window.
* Better PowerShell support.
* Bug fixes.
2020-01-21 04:33:16 -05:00
Tim Pope
29153d5ba1 Provide git config --get-all wrapper 2020-01-20 15:42:54 -05:00
Tim Pope
8d4b85ed09 Replace --format with --pretty=format for older Git 2020-01-20 15:42:54 -05:00
Tim Pope
708595d996 Fix comment typo 2020-01-20 15:42:45 -05:00
Chris DeLuca
dc6719e7b0 Fix :Gdiffsplit! help typo 2020-01-20 10:27:14 -05:00
Tim Pope
c48db08e4e Use appropriate slashes for :Dispatch working directory 2020-01-20 10:27:00 -05:00
Tim Pope
2ebdeef9e0 Change :Git --paginate from :terminal to temp buffer
The --paginate option to Git forces it to use $PAGER rather than its own
configuration, so it makes perfect sense for Fugitive to take on the
role of the pager.

This also removes the same behavior for --no-pager, allowing the command
to run with :! instead.  I really don't like sudden backwards
incompatible changes like this, but the old behavior was based on a bad
understanding of how these options work, and I'd rather rip the band-aid
off sooner rather than later.
2020-01-20 09:03:49 -05:00
Tim Pope
bc0b78ee84 Remove dead code 2020-01-20 08:42:02 -05:00
Tim Pope
5bcb42164e Specify shell command, not wrapper, in b:dispatch
This frees us up to change :Gfetch not to use :Make.
2020-01-20 07:28:55 -05:00
Tim Pope
1a6934fc7e Fix FugitiveParse() return value documentation 2020-01-20 07:03:48 -05:00
Tim Pope
397fb99921 Provide FugitiveBlob style autocommands
This is the real replacement for fugitive#buffer().type(), as the only
real world use of that seemed to be in BufReadPost autocommands.
2020-01-20 05:52:22 -05:00
Tim Pope
5b1213d0db Fix :Gmerge 2020-01-20 05:52:22 -05:00
Tim Pope
ae52b5a96a Better PowerShell support 2020-01-18 01:30:43 -08:00
Tim Pope
9f032ed040 Prevent alternate buffer change loading Fugitive buffer 2020-01-18 00:51:38 -05:00
Tim Pope
d428032600 Fix netrw gx in Fugitive buffers AGAIN 2020-01-18 00:45:17 -05:00
Tim Pope
c83355d5c5 Accomodate loading Fugitive buffers in popup window
Closes https://github.com/tpope/vim-fugitive/issues/1418
2020-01-15 01:26:31 -05:00
Tim Pope
4732bb964a Generalize :Git environment variable overrides 2020-01-14 22:56:11 -05:00
Tim Pope
3e32a55619 Avoid false positives on rebase todo handler 2020-01-14 22:56:11 -05:00
Tim Pope
a65db6fcf8 Move :Gmerge warning to top level function
The old location isn't long for this world.
2020-01-14 22:56:11 -05:00
Tim Pope
305337a9c1 Begin phasing out :Git --paginate special case
This was a mistake.  I took --paginate to be the opposite of --no-pager,
but it forces $PAGER over $GIT_PAGER and pager configuration.  This
defeats the purpose of using it as way to trick :Git into running a
regular command with :terminal.

References https://github.com/tpope/vim-fugitive/issues/1415
2020-01-14 20:43:21 -05:00
Tim Pope
6a638f2392 Match "* Unmerged path" in mergetool
This happens in leiu of a diff when one of the commits adds (or
presumably removes) the file rather than changing it.
2020-01-12 18:53:21 -05:00
Tim Pope
6bc345f6f1 Map cmt to :Git mergetool
References https://github.com/tpope/vim-fugitive/issues/1329
2020-01-10 20:29:58 -05:00
Tim Pope
def982ac4d Softly deprecate :Gmerge for quickfix conflicts 2020-01-10 20:29:15 -05:00
Tim Pope
5fe99dc5df Provide interface for subcommands to set execution options 2020-01-10 19:54:06 -05:00
Tim Pope
394c925381 Fix netrw gx in Fugitive buffers 2020-01-09 21:34:35 -05:00
Tim Pope
4074727343 Really fix packed ref handling in :Gbrowse
References https://github.com/tpope/vim-fugitive/issues/1428
2020-01-06 16:00:21 -05:00
Tim Pope
cfa7bdc82a Handle packed refs in :Gbrowse
Closes https://github.com/tpope/vim-fugitive/issues/1428
2020-01-06 13:33:49 -05:00
Tim Pope
ddd64fc4c5 Provide :Git difftool and :Git mergetool
Closes https://github.com/tpope/vim-fugitive/issues/132
2020-01-05 21:41:18 -05:00
4 changed files with 358 additions and 81 deletions

View File

@@ -33,6 +33,9 @@ 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 list. Give them a range (e.g., using visual mode and `:'<,'>Gclog`) to
iterate over every change to that portion of the current file. 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 `: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 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. and you never get any warnings about the file changing outside Vim.
@@ -89,9 +92,8 @@ 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 automatically if you have `ssh-askpass` or `git-gui` installed; otherwise it's
your responsibility to set this up. your responsibility to set this up.
As an absolute last resort, you can invoke `:Git --paginate push`. Fugitive If you absolutely must type in your password by hand, sidestep Fugitive and
recognizes the pagination request and fires up a `:terminal`, which allows for use `:terminal git push`.
interactive password entry.
[credentials caching]: https://help.github.com/en/articles/caching-your-github-password-in-git [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 endfunction
function! s:winshell() abort function! s:winshell() abort
return has('win32') && &shellcmdflag =~# '^/\|^-Command$' return has('win32') && &shellcmdflag !~# '^-'
endfunction endfunction
function! s:shellesc(arg) abort function! s:shellesc(arg) abort
@@ -225,6 +225,7 @@ function! s:QuickfixStream(nr, event, title, cmd, first, callback, ...) abort
let contexts = map(copy(buffer), 'get(v:val, "context", {})') let contexts = map(copy(buffer), 'get(v:val, "context", {})')
lockvar contexts lockvar contexts
call extend(opts.context.items, contexts) call extend(opts.context.items, contexts)
unlet contexts
call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a') call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
redraw redraw
endif endif
@@ -413,17 +414,24 @@ function! fugitive#PrepareDirEnvArgv(...) abort
return [dir, env, cmd] return [dir, env, cmd]
endfunction 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 function! s:BuildShell(dir, env, args) abort
let cmd = copy(a:args) let cmd = copy(a:args)
let tree = s:Tree(a:dir) let tree = s:Tree(a:dir)
let pre = '' let pre = s:BuildEnvPrefix(a:env)
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 if empty(tree) || index(cmd, '--') == len(cmd) - 1
call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir)) call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
elseif fugitive#GitVersion(1, 8, 5) elseif fugitive#GitVersion(1, 8, 5)
@@ -1259,17 +1267,16 @@ function! fugitive#setfperm(url, perm) abort
endfunction endfunction
function! s:TempCmd(out, cmd) abort function! s:TempCmd(out, cmd) abort
let prefix = ''
try try
let cmd = (type(a:cmd) == type([]) ? fugitive#Prepare(a:cmd) : a:cmd) let cmd = (type(a:cmd) == type([]) ? fugitive#Prepare(a:cmd) : a:cmd)
let redir = ' > ' . a:out let redir = ' > ' . a:out
if s:winshell() if (s:winshell() || &shellcmdflag ==# '-Command') && !has('nvim')
let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^' let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
return s:SystemError('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"') return s:SystemError('cmd /c "' . s:gsub(cmd, '[<>%]', cmd_escape_char . '&') . redir . '"')
elseif &shell =~# 'fish' elseif &shell =~# 'fish'
return s:SystemError(' begin;' . prefix . cmd . redir . ';end ') return s:SystemError(' begin;' . cmd . redir . ';end ')
else else
return s:SystemError(' (' . prefix . cmd . redir . ') ') return s:SystemError(' (' . cmd . redir . ') ')
endif endif
endtry endtry
endfunction endfunction
@@ -1554,28 +1561,13 @@ function! s:ReplaceCmd(cmd) abort
if exec_error if exec_error
call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd)) call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
endif endif
let temp = s:Resolve(temp) silent exe 'keepalt $read ++edit' s:fnameescape(temp)
let fn = expand('%:p') silent keepjumps 1delete _
silent exe 'keepalt file '.temp call delete(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 endfunction
function! s:QueryLog(refspec) abort function! s:QueryLog(refspec) abort
let lines = s:LinesError(['log', '-n', '256', '--format=%h%x09%s', a:refspec, '--'])[0] let lines = s:LinesError(['log', '-n', '256', '--pretty=format:%h%x09%s', a:refspec, '--'])[0]
call map(lines, 'split(v:val, "\t")') call map(lines, 'split(v:val, "\t")')
call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}') call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
return lines return lines
@@ -1834,7 +1826,8 @@ function! fugitive#BufReadStatus() abort
if push !=# pull if push !=# pull
call s:AddHeader('Push', push) call s:AddHeader('Push', push)
endif endif
if empty(s:Tree()) let tree = s:Tree()
if empty(tree)
call s:AddHeader('Bare', 'yes') call s:AddHeader('Bare', 'yes')
endif endif
call s:AddSection('Rebasing ' . rebasing_head, rebasing) call s:AddSection('Rebasing ' . rebasing_head, rebasing)
@@ -1852,7 +1845,8 @@ function! fugitive#BufReadStatus() abort
if &bufhidden ==# '' if &bufhidden ==# ''
setlocal bufhidden=delete setlocal bufhidden=delete
endif endif
let b:dispatch = ':Gfetch --all' let b:dispatch = '-compiler=git -dir=' . s:fnameescape(FugitiveVimPath(len(tree) ? tree : s:Dir())) .
\ ' ' . s:UserCommand() . ' ' . s:shellesc(s:AskPassArgs(s:Dir())) . ' fetch --all'
call fugitive#MapJumps() call fugitive#MapJumps()
call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>') 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>') call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
@@ -1915,7 +1909,7 @@ function! fugitive#BufReadStatus() abort
endfor endfor
let b:fugitive_reltime = reltime() let b:fugitive_reltime = reltime()
return '' return 'silent ' . s:DoAutocmd('User FugitiveIndex')
catch /^fugitive:/ catch /^fugitive:/
return 'echoerr ' . string(v:exception) return 'echoerr ' . string(v:exception)
endtry endtry
@@ -2018,7 +2012,7 @@ function! fugitive#BufReadCmd(...) abort
setlocal endofline setlocal endofline
try try
silent doautocmd BufReadPre silent exe s:DoAutocmd('BufReadPre')
if b:fugitive_type ==# 'tree' if b:fugitive_type ==# 'tree'
let b:fugitive_display_format = b:fugitive_display_format % 2 let b:fugitive_display_format = b:fugitive_display_format % 2
if b:fugitive_display_format if b:fugitive_display_format
@@ -2078,8 +2072,16 @@ function! fugitive#BufReadCmd(...) abort
endtry endtry
setlocal modifiable 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') . return 'silent ' . s:DoAutocmd('BufReadPost') .
\ (modifiable ? '' : '|setl nomodifiable') \ (modifiable ? '' : '|setl nomodifiable') . '|silent ' .
\ s:DoAutocmd('User Fugitive' . substitute(b:fugitive_type, '^\l', '\u&', ''))
catch /^fugitive:/ catch /^fugitive:/
return 'echoerr ' . string(v:exception) return 'echoerr ' . string(v:exception)
endtry endtry
@@ -2170,19 +2172,26 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help' if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
try try
exe s:DirCheck(dir) exe s:DirCheck(dir)
return 'exe ' . string(s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])) . after 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
endif
catch /^fugitive:/ catch /^fugitive:/
return 'echoerr ' . string(v:exception) return 'echoerr ' . string(v:exception)
endtry endtry
else
let opts = {}
endif endif
if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' || if a:bang || args[0] =~# '^-p$\|^--paginate$\|diff\%(tool\)\@!\|log\|^show$' ||
\ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') || \ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
\ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web') \ (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 return s:OpenExec((a:line2 > 0 ? a:line2 : '') . (a:line2 ? 'split' : 'edit'), a:mods, args, dir) . after
endif endif
if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') || 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 mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : '' let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : ''
if has('nvim') if has('nvim')
@@ -2193,13 +2202,14 @@ 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 return 'exe ' . string(mods . 'terminal ' . (a:line2 ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . assign . after
endif endif
endif endif
let env = get(opts, 'env', {})
if has('gui_running') && !has('win32') if has('gui_running') && !has('win32')
call insert(args, '--no-pager') call insert(args, '--no-pager')
endif endif
let pre = '' if has('nvim')
if has('nvim') && executable('env') let env.GIT_TERMINAL_PROMPT = '0'
let pre .= 'env GIT_TERMINAL_PROMPT=0 '
endif endif
let pre = s:BuildEnvPrefix(env)
return 'exe ' . string('noautocmd !' . escape(pre . s:UserCommand(dir, args), '!#%')) . return 'exe ' . string('noautocmd !' . escape(pre . s:UserCommand(dir, args), '!#%')) .
\ '|call fugitive#ReloadStatus(' . string(dir) . ', 1)' . \ '|call fugitive#ReloadStatus(' . string(dir) . ', 1)' .
\ after \ after
@@ -3373,7 +3383,7 @@ function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir) let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
let outfile = tempname() let outfile = tempname()
try try
if s:winshell() if s:winshell() || &shellcmdflag ==# '-Command'
let command = 'set GIT_EDITOR=false& ' let command = 'set GIT_EDITOR=false& '
else else
let command = 'env GIT_EDITOR=false ' let command = 'env GIT_EDITOR=false '
@@ -3528,7 +3538,7 @@ function! s:RebaseSequenceAborter() abort
\ 'echo exec false | cat - "$1" > "$1.fugitive"', \ 'echo exec false | cat - "$1" > "$1.fugitive"',
\ 'mv "$1.fugitive" "$1"'], \ 'mv "$1.fugitive" "$1"'],
\ temp) \ temp)
let s:rebase_sequence_aborter = temp let s:rebase_sequence_aborter = FugitiveGitPath(temp)
endif endif
return s:rebase_sequence_aborter return s:rebase_sequence_aborter
endfunction endfunction
@@ -3661,13 +3671,7 @@ function! s:MergeRebase(cmd, bang, mods, args, ...) abort
\ . "%+EXUNG \u0110\u1ed8T %.%#," \ . "%+EXUNG \u0110\u1ed8T %.%#,"
\ . "%+E\u51b2\u7a81 %.%#," \ . "%+E\u51b2\u7a81 %.%#,"
\ . 'U%\t%f' \ . 'U%\t%f'
if a:cmd =~# '^merge' && empty(args) && let cmd = s:UserCommand(dir, argv)
\ (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') if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
let old_sequence_editor = $GIT_SEQUENCE_EDITOR let old_sequence_editor = $GIT_SEQUENCE_EDITOR
let $GIT_SEQUENCE_EDITOR = 'true' let $GIT_SEQUENCE_EDITOR = 'true'
@@ -3757,6 +3761,13 @@ function! s:RebaseClean(file) abort
endfunction endfunction
function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort 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) return s:MergeRebase('merge', a:bang, a:mods, a:args)
endfunction endfunction
@@ -3776,7 +3787,7 @@ endfunction
augroup fugitive_merge augroup fugitive_merge
autocmd! autocmd!
autocmd VimLeavePre,BufDelete git-rebase-todo autocmd VimLeavePre,BufDelete git-rebase-todo
\ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' | \ if type(getbufvar(+expand('<abuf>'), 'fugitive_rebase_shas')) == type({}) && getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
\ call s:RebaseClean(expand('<afile>')) | \ call s:RebaseClean(expand('<afile>')) |
\ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 | \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
\ let s:rebase_continue = [FugitiveGitDir(+expand('<abuf>')), 1] | \ let s:rebase_continue = [FugitiveGitDir(+expand('<abuf>')), 1] |
@@ -3792,6 +3803,243 @@ augroup fugitive_merge
\ endif \ endif
augroup END augroup END
" Section: :Git difftool, :Git mergetool
function! s:ToolItems(state, from, to, offsets, text, ...) abort
let items = []
for i in range(len(a:state.diff))
let diff = a:state.diff[i]
let path = (i == len(a:state.diff) - 1) ? a:to : a:from
if empty(path)
return []
endif
let item = {
\ 'valid': a:0 ? a:1 : 1,
\ 'filename': diff.filename . FugitiveVimPath(path),
\ 'lnum': matchstr(get(a:offsets, i), '\d\+'),
\ 'text': a:text}
if len(get(diff, 'module', ''))
let item.module = diff.module . path
endif
call add(items, item)
endfor
let diff = items[0:-2]
let items[-1].context = {'diff': items[0:-2]}
return [items[-1]]
endfunction
function! s:ToolToFrom(str) abort
if a:str =~# ' => '
let str = a:str =~# '{.* => .*}' ? a:str : '{' . a:str . '}'
return [substitute(str, '{.* => \(.*\)}', '\1', ''),
\ substitute(str, '{\(.*\) => .*}', '\1', '')]
else
return [a:str, a:str]
endif
endfunction
function! s:ToolParse(state, line) abort
if type(a:line) !=# type('') || a:state.mode ==# 'hunk' && a:line =~# '^[ +-]'
return []
elseif a:line =~# '^diff '
let a:state.mode = 'diffhead'
let a:state.from = ''
let a:state.to = ''
elseif a:state.mode ==# 'diffhead' && a:line =~# '^--- [^/]'
let a:state.from = a:line[4:-1]
let a:state.to = a:state.from
elseif a:state.mode ==# 'diffhead' && a:line =~# '^+++ [^/]'
let a:state.to = a:line[4:-1]
if empty(get(a:state, 'from', ''))
let a:state.from = a:state.to
endif
elseif a:line[0] ==# '@'
let a:state.mode = 'hunk'
if has_key(a:state, 'from')
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")
return s:ToolItems(a:state, files[0], files[-1], [], a:state.name_only ? '' : status)
elseif a:line =~# '^ \S.* |'
" --stat
let [_, to, changes; __] = matchlist(a:line, '^ \(.\{-\}\) \+|\zs \(.*\)$')
let [to, from] = s:ToolToFrom(to)
return s:ToolItems(a:state, from, to, [], changes)
elseif a:line =~# '^ *\([0-9.]\+%\) .'
" --dirstat
let [_, changes, to; __] = matchlist(a:line, '^ *\([0-9.]\+%\) \(.*\)')
return s:ToolItems(a:state, to, to, [], changes)
elseif a:line =~# '^\(\d\+\|-\)\t\(\d\+\|-\)\t.'
" --numstat
let [_, add, remove, to; __] = matchlist(a:line, '^\(\d\+\|-\)\t\(\d\+\|-\)\t\(.*\)')
let [to, from] = s:ToolToFrom(to)
return s:ToolItems(a:state, from, to, [], add ==# '-' ? 'Binary file' : '+' . add . ' -' . remove, add !=# '-')
elseif a:state.mode !=# 'diffhead' && a:state.mode !=# 'hunk' && len(a:line) || a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
return [{'text': a:line}]
endif
return []
endfunction
function! s:ToolStream(dir, line1, line2, range, bang, mods, args, state, title) abort
let i = 0
let argv = copy(a:args)
let prompt = 1
let state = a:state
while i < len(argv)
let match = matchlist(argv[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
if len(match) && len(match[2])
call insert(argv, match[1])
let argv[i+1] = '-' . match[2]
continue
endif
let arg = argv[i]
if arg =~# '^-t$\|^--tool=\|^--tool-help$\|^--help$'
return {}
elseif arg =~# '^-y$\|^--no-prompt$'
let prompt = 0
call remove(argv, i)
continue
elseif arg ==# '--prompt'
let prompt = 1
call remove(argv, i)
continue
elseif arg =~# '^--\%(no-\)\=\(symlinks\|trust-exit-code\|gui\)$'
call remove(argv, i)
continue
elseif arg ==# '--'
break
endif
let i += 1
endwhile
let a:state.mode = 'init'
let a:state.from = ''
let a:state.to = ''
let exec = s:UserCommandList(a:dir) + ['--no-pager', '-c', 'diff.context=0', 'diff', '--no-ext-diff', '--no-color', '--no-prefix'] + argv
if prompt
return s:QuickfixStream(a:line2, 'difftool', a:title, exec, !a:bang, s:function('s:ToolParse'), a:state)
else
let filename = ''
let cmd = []
let tabnr = tabpagenr() + 1
for line in split(s:SystemError(s:shellesc(exec))[0], "\n")
for item in s:ToolParse(a:state, line)
if len(get(item, 'filename', '')) && item.filename != filename
call add(cmd, 'tabedit ' . s:fnameescape(item.filename))
for i in reverse(range(len(get(item.context, 'diff', []))))
call add(cmd, (i ? 'rightbelow' : 'leftabove') . ' vert Gdiffsplit! ' . s:fnameescape(item.context.diff[i].filename))
endfor
call add(cmd, 'wincmd =')
let filename = item.filename
endif
endfor
endfor
return join(cmd, '|') . (empty(cmd) ? '' : '|' . tabnr . 'tabnext')
endif
endfunction
function! s:MergetoolSubcommand(line1, line2, range, bang, mods, args) abort
let dir = s:Dir()
let i = 0
let argv = copy(a:args)
let prompt = 1
let title = ':Git mergetool' . (len(a:args) ? ' ' . s:fnameescape(a:args) : '')
let cmd = ['diff', '--diff-filter=U']
let state = {'name_only': 0}
let state.diff = [{'prefix': ':2:', 'module': ':2:'}, {'prefix': ':3:', 'module': ':3:'}, {'prefix': ':(top)'}]
call map(state.diff, 'extend(v:val, {"filename": fugitive#Find(v:val.prefix, dir)})')
return s:ToolStream(dir, a:line1, a:line2, a:range, a:bang, a:mods, ['--diff-filter=U'] + a:args, state, title)
endfunction
function! s:DifftoolSubcommand(line1, line2, range, bang, mods, args) abort
let dir = s:Dir()
let i = 0
let argv = copy(a:args)
let commits = []
let cached = 0
let reverse = 1
let prompt = 1
let state = {'name_only': 0}
let merge_base_against = {}
let dash = (index(argv, '--') > i ? ['--'] : [])
while i < len(argv)
let match = matchlist(argv[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
if len(match) && len(match[2])
call insert(argv, match[1])
let argv[i+1] = '-' . match[2]
continue
endif
let arg = argv[i]
if arg ==# '--cached'
let cached = 1
elseif arg ==# '-R'
let reverse = 1
elseif arg ==# '--name-only'
let state.name_only = 1
let argv[0] = '--name-status'
elseif arg ==# '--'
break
elseif arg !~# '^-\|^\.\.\=\%(/\|$\)'
let parsed = s:LinesError(['rev-parse', '--revs-only', substitute(arg, ':.*', '', '')] + dash)[0]
call map(parsed, '{"uninteresting": v:val =~# "^\\^", "prefix": substitute(v:val, "^\\^", "", "") . ":"}')
let merge_base_against = {}
if arg =~# '\.\.\.' && len(parsed) > 2
let display = map(split(arg, '\.\.\.', 1), 'empty(v:val) ? "@" : v:val')
if len(display) == 2
let parsed[0].module = display[1] . ':'
let parsed[1].module = display[0] . ':'
endif
let parsed[2].module = arg . ':'
if empty(commits)
let merge_base_against = parsed[0]
let parsed = [parsed[2]]
endif
elseif arg =~# '\.\.' && len(parsed) == 2
let display = map(split(arg, '\.\.', 1), 'empty(v:val) ? "@" : v:val')
if len(display) == 2
let parsed[0].module = display[0] . ':'
let parsed[1].module = display[1] . ':'
endif
elseif len(parsed) == 1
let parsed[0].module = arg . ':'
endif
call extend(commits, parsed)
endif
let i += 1
endwhile
let title = ':Git difftool' . (len(a:args) ? ' ' . s:fnameescape(a:args) : '')
if len(merge_base_against)
call add(commits, merge_base_against)
endif
let commits = filter(copy(commits), 'v:val.uninteresting') + filter(commits, '!v:val.uninteresting')
if cached
if empty(commits)
call add(commits, {'prefix': '@:', 'module': '@:'})
endif
call add(commits, {'prefix': ':0:', 'module': ':0:'})
elseif len(commits) < 2
call add(commits, {'prefix': ':(top)'})
if len(commits) < 2
call insert(commits, {'prefix': ':0:', 'module': ':0:'})
endif
endif
if reverse
let commits = [commits[-1]] + repeat([commits[0]], len(commits) - 1)
call reverse(commits)
endif
if len(commits) > 2
call add(commits, remove(commits, 0))
endif
call map(commits, 'extend(v:val, {"filename": fugitive#Find(v:val.prefix, dir)})')
let state.diff = commits
return s:ToolStream(dir, a:line1, a:line2, a:range, a:bang, a:mods, argv, state, title)
endfunction
" Section: :Ggrep, :Glog " Section: :Ggrep, :Glog
if !exists('g:fugitive_summary_format') if !exists('g:fugitive_summary_format')
@@ -3846,11 +4094,7 @@ function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
call add(cmd, '--column') call add(cmd, '--column')
endif endif
let tree = s:Tree(dir) let tree = s:Tree(dir)
if type(a:args) == type([]) let args = a:args
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 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 name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
@@ -3873,9 +4117,9 @@ function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
redraw redraw
endif endif
if !a:bang && !empty(list) if !a:bang && !empty(list)
return (listnr < 0 ? 'c' : 'l').'first' . after return (listnr < 0 ? 'c' : 'l').'first'
else else
return after[1:-1] return ''
endif endif
endfunction endfunction
@@ -4170,13 +4414,7 @@ function! s:OpenExec(cmd, mods, args, ...) abort
let dir = a:0 ? s:Dir(a:1) : s:Dir() let dir = a:0 ? s:Dir(a:1) : s:Dir()
let temp = tempname() let temp = tempname()
let columns = get(g:, 'fugitive_columns', 80) let columns = get(g:, 'fugitive_columns', 80)
if columns <= 0 let env = s:BuildEnvPrefix({'COLUMNS': columns})
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), '!#%') . silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
\ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1') \ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
redraw! redraw!
@@ -5472,7 +5710,12 @@ function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, args) abo
endif endif
let i = 0 let i = 0
while commit =~# '^ref: ' && i < 10 while commit =~# '^ref: ' && i < 10
let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0] 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 i -= 1 let i -= 1
endwhile endwhile
endif endif
@@ -5694,6 +5937,7 @@ function! fugitive#MapJumps(...) abort
nnoremap <buffer> cm<Space> :Git merge<Space> nnoremap <buffer> cm<Space> :Git merge<Space>
nnoremap <buffer> cm<CR> :Git merge<CR> nnoremap <buffer> cm<CR> :Git merge<CR>
nnoremap <buffer> cmt :Git mergetool
nnoremap <buffer> <silent> cm? :help fugitive_cm<CR> nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
nnoremap <buffer> cz<Space> :Git stash<Space> nnoremap <buffer> cz<Space> :Git stash<Space>

View File

@@ -20,10 +20,9 @@ that are part of Git repositories).
:G {args} but chdir to the repository tree first. For some :G {args} but chdir to the repository tree first. For some
subcommands, a Fugitive command is called instead. subcommands, a Fugitive command is called instead.
*:Git!*
:Git! {args} Like |:Git|, but capture the output into a temp file, :Git! {args} Like |:Git|, but capture the output into a temp file,
:Git --no-pager {args} and |:split| that temp file. Use :0Git to :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 -p {args} |:edit| the temp file instead. A temp file is always
used for diff and log commands. used for diff and log commands.
*:Gstatus* *:Gstatus*
@@ -103,9 +102,23 @@ that are part of Git repositories).
:Glgrep[!] [args] |:lgrep|[!] with git-grep as 'grepprg'. :Glgrep[!] [args] |:lgrep|[!] with git-grep as 'grepprg'.
:0Git[!] grep [args] :0Git[!] grep [args]
*:Git-difftool*
:Git[!] difftool [args] Invoke `git diff [args]` and load the changes into the
quickfix list. Each changed hunk gets a separate
quickfix entry unless you pass an option like
--name-only or --name-status. Jumps to the first
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
appropriate commit.
*:Git-mergetool*
:Git mergetool [args] Like |:Git-difftool|, but target merge conflicts.
*:Gclog* *:Glog* *:Gclog* *:Glog*
:Gclog[!] [args] Use git-log [args] to load the commit history into the :Gclog[!] [args] Use git-log [args] to load the commit history into the
:Glog[!] [args] |quickfix| list. Jump to the first commit unless [!] :Glog[!] [args] |quickfix| list. Jumps to the first commit unless [!]
is given. is given.
:{range}Gclog[!] [args] Use git-log -L to load previous revisions of the given :{range}Gclog[!] [args] Use git-log -L to load previous revisions of the given
@@ -194,7 +207,7 @@ that are part of Git repositories).
focus on the current window. During a merge conflict, focus on the current window. During a merge conflict,
this is a three-way diff against the "ours" and this is a three-way diff against the "ours" and
"theirs" ancestors. Additional d2o and d3o maps are "theirs" ancestors. Additional d2o and d3o maps are
provided to to obtain the hunk from the "ours" or provided to obtain the hunk from the "ours" or
"theirs" ancestor, respectively. "theirs" ancestor, respectively.
:Gdiffsplit! {object} Like |:Gdiffsplit|, but retain focus on the current :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 " fugitive.vim - A Git wrapper so awesome, it should be illegal
" Maintainer: Tim Pope <http://tpo.pe/> " Maintainer: Tim Pope <http://tpo.pe/>
" Version: 3.1 " Version: 3.2
" GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim " GetLatestVimScripts: 2975 1 :AutoInstall: fugitive.vim
if exists('g:loaded_fugitive') if exists('g:loaded_fugitive')
@@ -66,8 +66,8 @@ function! FugitivePath(...) abort
endfunction endfunction
" FugitiveParse() takes a fugitive:// URL and returns a 2 element list " FugitiveParse() takes a fugitive:// URL and returns a 2 element list
" containing the Git dir and an object name ("commit:file"). It's effectively " containing an object name ("commit:file") and the Git dir. It's effectively
" then inverse of FugitiveFind(). " the inverse of FugitiveFind().
function! FugitiveParse(...) abort function! FugitiveParse(...) abort
let path = s:Slash(a:0 ? a:1 : @%) let path = s:Slash(a:0 ? a:1 : @%)
if path !~# '^fugitive:' if path !~# '^fugitive:'
@@ -103,6 +103,24 @@ function! FugitiveConfig(...) abort
endif endif
endfunction 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 function! FugitiveRemoteUrl(...) abort
return fugitive#RemoteUrl(a:0 ? a:1 : '', FugitiveGitDir(a:0 > 1 ? a:2 : -1)) return fugitive#RemoteUrl(a:0 ? a:1 : '', FugitiveGitDir(a:0 > 1 ? a:2 : -1))
endfunction endfunction