1
0
Fork 0
mirror of synced 2024-11-25 10:15:35 -05:00
ultimate-vim/sources_non_forked/vim-fugitive/autoload/fugitive.vim

8133 lines
272 KiB
VimL
Raw Normal View History

2018-06-14 06:31:12 -04:00
" Location: autoload/fugitive.vim
" Maintainer: Tim Pope <http://tpo.pe/>
2021-07-30 16:52:54 -04:00
" The functions contained within this file are for internal use only. For the
" official API, see the commented functions in plugin/fugitive.vim.
2018-06-14 06:31:12 -04:00
if exists('g:autoloaded_fugitive')
finish
endif
let g:autoloaded_fugitive = 1
" Section: Utility
function! s:function(name) abort
2019-11-16 10:28:42 -05:00
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '.*\zs<SNR>\d\+_'),''))
2018-06-14 06:31:12 -04:00
endfunction
function! s:sub(str,pat,rep) abort
return substitute(a:str,'\v\C'.a:pat,a:rep,'')
endfunction
function! s:gsub(str,pat,rep) abort
return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
endfunction
2018-08-25 12:13:42 -04:00
function! s:Uniq(list) abort
let i = 0
let seen = {}
while i < len(a:list)
let str = string(a:list[i])
if has_key(seen, str)
call remove(a:list, i)
else
let seen[str] = 1
let i += 1
endif
endwhile
return a:list
endfunction
2021-10-11 05:30:43 -04:00
function! s:JoinChomp(list) abort
if empty(a:list[-1])
return join(a:list[0:-2], "\n")
else
return join(a:list, "\n")
endif
endfunction
2018-06-14 06:31:12 -04:00
function! s:winshell() abort
2020-01-28 21:07:36 -05:00
return has('win32') && &shellcmdflag !~# '^-'
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:WinShellEsc(arg) abort
if type(a:arg) == type([])
2021-06-23 05:57:12 -04:00
return join(map(copy(a:arg), 's:WinShellEsc(v:val)'))
2020-04-25 21:56:16 -04:00
elseif a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
return a:arg
else
return '"' . s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"') . '"'
endif
endfunction
2018-06-14 06:31:12 -04:00
function! s:shellesc(arg) abort
2019-08-22 11:36:17 -04:00
if type(a:arg) == type([])
return join(map(copy(a:arg), 's:shellesc(v:val)'))
2020-04-25 21:56:16 -04:00
elseif a:arg =~# '^[A-Za-z0-9_/:.-]\+$'
2018-06-14 06:31:12 -04:00
return a:arg
elseif s:winshell()
2020-04-25 21:56:16 -04:00
return '"' . s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"') . '"'
2018-06-14 06:31:12 -04:00
else
return shellescape(a:arg)
endif
endfunction
function! s:fnameescape(file) abort
2019-08-22 11:36:17 -04:00
if type(a:file) == type([])
return join(map(copy(a:file), 's:fnameescape(v:val)'))
2022-08-08 09:45:56 -04:00
else
2018-06-14 06:31:12 -04:00
return fnameescape(a:file)
2022-08-08 09:45:56 -04:00
endif
endfunction
function! fugitive#UrlDecode(str) abort
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
endfunction
function! s:UrlEncode(str) abort
return substitute(a:str, '[%#?&;+=\<> [:cntrl:]]', '\=printf("%%%02X", char2nr(submatch(0)))', 'g')
endfunction
function! s:PathUrlEncode(str) abort
return substitute(a:str, '[%#?[:cntrl:]]', '\=printf("%%%02X", char2nr(submatch(0)))', 'g')
endfunction
function! s:PathJoin(prefix, str) abort
if a:prefix =~# '://'
return a:prefix . s:PathUrlEncode(a:str)
2018-06-14 06:31:12 -04:00
else
2022-08-08 09:45:56 -04:00
return a:prefix . a:str
2018-06-14 06:31:12 -04:00
endif
endfunction
function! s:throw(string) abort
2019-08-22 11:36:17 -04:00
throw 'fugitive: '.a:string
2018-06-14 06:31:12 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! s:VersionCheck() abort
2022-08-08 09:45:56 -04:00
if v:version < 704
return 'return ' . string('echoerr "fugitive: Vim 7.4 or newer required"')
2021-05-05 04:25:00 -04:00
elseif empty(fugitive#GitVersion())
2021-10-11 05:30:43 -04:00
let exe = get(s:GitCmd(), 0, '')
if len(exe) && !executable(exe)
return 'return ' . string('echoerr "fugitive: cannot find ' . string(exe) . ' in PATH"')
endif
2021-05-05 04:25:00 -04:00
return 'return ' . string('echoerr "fugitive: cannot execute Git"')
elseif !fugitive#GitVersion(1, 8, 5)
return 'return ' . string('echoerr "fugitive: Git 1.8.5 or newer required"')
else
return ''
endif
endfunction
2021-07-30 16:52:54 -04:00
let s:worktree_error = "core.worktree is required when using an external Git dir"
2019-08-22 11:36:17 -04:00
function! s:DirCheck(...) abort
2021-05-05 04:25:00 -04:00
let vcheck = s:VersionCheck()
if !empty(vcheck)
return vcheck
2021-07-30 16:52:54 -04:00
endif
2021-10-11 05:30:43 -04:00
let dir = call('FugitiveGitDir', a:000)
2021-07-30 16:52:54 -04:00
if !empty(dir) && FugitiveWorkTree(dir, 1) is# 0
return 'return ' . string('echoerr "fugitive: ' . s:worktree_error . '"')
elseif !empty(dir)
2019-11-16 10:28:42 -05:00
return ''
elseif empty(bufname(''))
return 'return ' . string('echoerr "fugitive: working directory does not belong to a Git repository"')
else
return 'return ' . string('echoerr "fugitive: file does not belong to a Git repository"')
2019-08-22 11:36:17 -04:00
endif
2018-06-14 06:31:12 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:Mods(mods, ...) abort
let mods = substitute(a:mods, '\C<mods>', '', '')
let mods = mods =~# '\S$' ? mods . ' ' : mods
if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>'
2021-10-11 05:30:43 -04:00
if a:1 ==# 'Edge'
if mods =~# '\<vertical\>' ? &splitright : &splitbelow
let mods = 'botright ' . mods
else
let mods = 'topleft ' . mods
endif
else
let mods = a:1 . ' ' . mods
endif
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
return substitute(mods, '\s\+', ' ', 'g')
2018-06-14 06:31:12 -04:00
endfunction
2021-05-05 04:25:00 -04:00
if exists('+shellslash')
2022-08-08 09:45:56 -04:00
let s:dir_commit_file = '\c^fugitive://\%(/[^/]\@=\)\=\(.\{-1,\}\)//\%(\(\x\{40,\}\|[0-3]\)\(/.*\)\=\)\=$'
2021-05-05 04:25:00 -04:00
function! s:Slash(path) abort
2019-08-22 11:36:17 -04:00
return tr(a:path, '\', '/')
2021-05-05 04:25:00 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:VimSlash(path) abort
return tr(a:path, '\/', &shellslash ? '//' : '\\')
endfunction
2021-05-05 04:25:00 -04:00
else
2022-08-08 09:45:56 -04:00
let s:dir_commit_file = '\c^fugitive://\(.\{-\}\)//\%(\(\x\{40,\}\|[0-3]\)\(/.*\)\=\)\=$'
2021-05-05 04:25:00 -04:00
function! s:Slash(path) abort
2018-07-04 06:53:25 -04:00
return a:path
2021-05-05 04:25:00 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:VimSlash(path) abort
return a:path
endfunction
2021-05-05 04:25:00 -04:00
endif
2018-07-04 06:53:25 -04:00
2022-08-08 09:45:56 -04:00
function! s:AbsoluteVimPath(...) abort
if a:0 && type(a:1) == type('')
let path = a:1
else
let path = bufname(a:0 && a:1 > 0 ? a:1 : '')
if getbufvar(a:0 && a:1 > 0 ? a:1 : '', '&buftype') !~# '^\%(nowrite\|acwrite\)\=$'
return path
endif
endif
if s:Slash(path) =~# '^/\|^\a\+:'
return path
else
return getcwd() . matchstr(getcwd(), '[\\/]') . path
endif
endfunction
2018-08-25 12:13:42 -04:00
function! s:Resolve(path) abort
let path = resolve(a:path)
if has('win32')
2022-08-08 09:45:56 -04:00
let path = s:VimSlash(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t'))
2018-08-25 12:13:42 -04:00
endif
return path
endfunction
2020-04-25 21:56:16 -04:00
function! s:FileIgnoreCase(for_completion) abort
return (exists('+fileignorecase') && &fileignorecase)
\ || (a:for_completion && exists('+wildignorecase') && &wildignorecase)
endfunction
2018-08-25 12:13:42 -04:00
function! s:cpath(path, ...) abort
2020-04-25 21:56:16 -04:00
if s:FileIgnoreCase(0)
2022-08-08 09:45:56 -04:00
let path = s:VimSlash(tolower(a:path))
2018-08-25 12:13:42 -04:00
else
2022-08-08 09:45:56 -04:00
let path = s:VimSlash(a:path)
2018-08-25 12:13:42 -04:00
endif
return a:0 ? path ==# s:cpath(a:1) : path
endfunction
2022-08-08 09:45:56 -04:00
let s:quote_chars = {
\ "\007": 'a', "\010": 'b', "\011": 't', "\012": 'n', "\013": 'v', "\014": 'f', "\015": 'r',
\ '"': '"', '\': '\'}
let s:unquote_chars = {
\ 'a': "\007", 'b': "\010", 't': "\011", 'n': "\012", 'v': "\013", 'f': "\014", 'r': "\015",
\ '"': '"', '\': '\'}
function! s:Quote(string) abort
let string = substitute(a:string, "[\001-\037\"\\\177]", '\="\\" . get(s:quote_chars, submatch(0), printf("%03o", char2nr(submatch(0))))', 'g')
if string !=# a:string
return '"' . string . '"'
else
return string
endif
endfunction
function! fugitive#Unquote(string) abort
let string = substitute(a:string, "\t*$", '', '')
if string =~# '^".*"$'
return substitute(string[1:-2], '\\\(\o\o\o\|.\)', '\=get(s:unquote_chars, submatch(1), iconv(nr2char("0" . submatch(1)), "utf-8", "latin1"))', 'g')
else
return string
endif
endfunction
2018-06-14 06:31:12 -04:00
let s:executables = {}
function! s:executable(binary) abort
if !has_key(s:executables, a:binary)
let s:executables[a:binary] = executable(a:binary)
endif
return s:executables[a:binary]
endfunction
2020-01-28 21:07:36 -05:00
if !exists('s:temp_scripts')
let s:temp_scripts = {}
endif
function! s:TempScript(...) abort
let body = join(a:000, "\n")
if !has_key(s:temp_scripts, body)
2020-12-04 16:15:32 -05:00
let s:temp_scripts[body] = tempname() . '.sh'
endif
let temp = s:temp_scripts[body]
if !filereadable(temp)
2020-01-28 21:07:36 -05:00
call writefile(['#!/bin/sh'] + a:000, temp)
endif
2020-12-04 16:15:32 -05:00
return FugitiveGitPath(temp)
2020-01-28 21:07:36 -05:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:DoAutocmd(...) abort
2022-08-08 09:45:56 -04:00
return join(map(copy(a:000), "'doautocmd <nomodeline>' . v:val"), '|')
2019-11-16 10:28:42 -05:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:Map(mode, lhs, rhs, ...) abort
2021-10-11 05:30:43 -04:00
let maps = []
2022-08-08 09:45:56 -04:00
let defer = a:0 && a:1 =~# '<unique>' || get(g:, 'fugitive_defer_to_existing_maps')
let flags = substitute(a:0 ? a:1 : '', '<unique>', '', '') . (a:rhs =~# '<Plug>' ? '' : '<script>') . '<nowait>'
2019-08-22 11:36:17 -04:00
for mode in split(a:mode, '\zs')
2022-08-08 09:45:56 -04:00
if a:0 <= 1
call add(maps, mode.'map <buffer>' . substitute(flags, '<unique>', '', '') . ' <Plug>fugitive:' . a:lhs . ' ' . a:rhs)
endif
2021-10-11 05:30:43 -04:00
let skip = 0
2019-08-22 11:36:17 -04:00
let head = a:lhs
let tail = ''
let keys = get(g:, mode.'remap', {})
if type(keys) == type([])
2021-10-11 05:30:43 -04:00
continue
2018-08-25 12:13:42 -04:00
endif
2019-08-22 11:36:17 -04:00
while !empty(head)
if has_key(keys, head)
let head = keys[head]
2021-10-11 05:30:43 -04:00
let skip = empty(head)
2019-08-22 11:36:17 -04:00
break
endif
let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
let head = substitute(head, '<[^<>]*>$\|.$', '', '')
endwhile
2022-08-08 09:45:56 -04:00
if !skip && (!defer || empty(mapcheck(head.tail, mode)))
call add(maps, mode.'map <buffer>' . flags . ' ' . head.tail . ' ' . a:rhs)
2021-10-28 15:48:21 -04:00
if a:0 > 1 && a:2
2019-08-22 11:36:17 -04:00
let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') .
\ '|sil! exe "' . mode . 'unmap <buffer> ' . head.tail . '"'
endif
2018-08-25 12:13:42 -04:00
endif
2019-08-22 11:36:17 -04:00
endfor
2021-10-11 05:30:43 -04:00
exe join(maps, '|')
return ''
2019-08-22 11:36:17 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! fugitive#Autowrite() abort
if &autowrite || &autowriteall
try
if &confirm
let reconfirm = 1
setglobal noconfirm
endif
silent! wall
finally
if exists('reconfirm')
setglobal confirm
endif
endtry
endif
return ''
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#Wait(job_or_jobs, ...) abort
2022-08-08 09:45:56 -04:00
let original = type(a:job_or_jobs) == type([]) ? copy(a:job_or_jobs) : [a:job_or_jobs]
let jobs = map(copy(original), 'type(v:val) ==# type({}) ? get(v:val, "job", "") : v:val')
2021-10-11 05:30:43 -04:00
call filter(jobs, 'type(v:val) !=# type("")')
let timeout_ms = a:0 ? a:1 : -1
if exists('*jobwait')
call map(copy(jobs), 'chanclose(v:val, "stdin")')
call jobwait(jobs, timeout_ms)
2022-08-08 09:45:56 -04:00
let jobs = map(copy(original), 'type(v:val) ==# type({}) ? get(v:val, "job", "") : v:val')
call filter(jobs, 'type(v:val) !=# type("")')
if len(jobs)
2021-10-11 05:30:43 -04:00
sleep 1m
endif
else
let sleep = has('patch-8.2.2366') ? 'sleep! 1m' : 'sleep 1m'
for job in jobs
2022-08-08 09:45:56 -04:00
if ch_status(job) ==# 'open'
2021-10-11 05:30:43 -04:00
call ch_close_in(job)
endif
endfor
let i = 0
for job in jobs
2022-08-08 09:45:56 -04:00
while ch_status(job) !~# '^closed$\|^fail$' || job_status(job) ==# 'run'
2021-10-11 05:30:43 -04:00
if i == timeout_ms
break
endif
let i += 1
exe sleep
endwhile
endfor
endif
return a:job_or_jobs
endfunction
function! s:JobVimExit(dict, callback, temp, job, status) abort
let a:dict.exit_status = a:status
let a:dict.stderr = readfile(a:temp . '.err', 'b')
call delete(a:temp . '.err')
let a:dict.stdout = readfile(a:temp . '.out', 'b')
call delete(a:temp . '.out')
call delete(a:temp . '.in')
call remove(a:dict, 'job')
call call(a:callback[0], [a:dict] + a:callback[1:-1])
endfunction
function! s:JobNvimExit(dict, callback, job, data, type) dict abort
let a:dict.stdout = self.stdout
let a:dict.stderr = self.stderr
let a:dict.exit_status = a:data
call remove(a:dict, 'job')
call call(a:callback[0], [a:dict] + a:callback[1:-1])
endfunction
function! s:JobExecute(argv, jopts, stdin, callback, ...) abort
let dict = a:0 ? a:1 : {}
let cb = len(a:callback) ? a:callback : [function('len')]
if exists('*jobstart')
call extend(a:jopts, {
\ 'stdout_buffered': v:true,
\ 'stderr_buffered': v:true,
\ 'on_exit': function('s:JobNvimExit', [dict, cb])})
2022-08-08 09:45:56 -04:00
try
let dict.job = jobstart(a:argv, a:jopts)
if !empty(a:stdin)
call chansend(dict.job, a:stdin)
call chanclose(dict.job, 'stdin')
endif
catch /^Vim\%((\a\+)\)\=:E475:/
let [dict.exit_status, dict.stdout, dict.stderr] = [122, [''], ['']]
endtry
elseif exists('*ch_close_in')
2021-10-11 05:30:43 -04:00
let temp = tempname()
call extend(a:jopts, {
\ 'out_io': 'file',
\ 'out_name': temp . '.out',
\ 'err_io': 'file',
\ 'err_name': temp . '.err',
\ 'exit_cb': function('s:JobVimExit', [dict, cb, temp])})
if a:stdin ==# ['']
let a:jopts.in_io = 'null'
elseif !empty(a:stdin)
let a:jopts.in_io = 'file'
let a:jopts.in_name = temp . '.in'
call writefile(a:stdin, a:jopts.in_name, 'b')
endif
let dict.job = job_start(a:argv, a:jopts)
2022-08-08 09:45:56 -04:00
if job_status(dict.job) ==# 'fail'
let [dict.exit_status, dict.stdout, dict.stderr] = [122, [''], ['']]
unlet dict.job
endif
2021-10-11 05:30:43 -04:00
elseif &shell !~# 'sh' || &shell =~# 'fish\|\%(powershell\|pwsh\)\%(\.exe\)\=$'
throw 'fugitive: Vim 8 or higher required to use ' . &shell
else
let cmd = s:shellesc(a:argv)
let outfile = tempname()
try
2022-08-08 09:45:56 -04:00
if len(a:stdin)
call writefile(a:stdin, outfile . '.in', 'b')
let cmd = ' (' . cmd . ' >' . outfile . ' <' . outfile . '.in) '
else
let cmd = ' (' . cmd . ' >' . outfile . ') '
endif
let dict.stderr = split(system(cmd), "\n", 1)
2021-10-11 05:30:43 -04:00
let dict.exit_status = v:shell_error
let dict.stdout = readfile(outfile, 'b')
call call(cb[0], [dict] + cb[1:-1])
finally
call delete(outfile)
2022-08-08 09:45:56 -04:00
call delete(outfile . '.in')
2021-10-11 05:30:43 -04:00
endtry
endif
if empty(a:callback)
call fugitive#Wait(dict)
endif
return dict
endfunction
2021-07-30 16:52:54 -04:00
function! s:add_methods(namespace, method_names) abort
for name in a:method_names
let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
endfor
endfunction
2018-08-25 12:13:42 -04:00
" Section: Git
2022-08-08 09:45:56 -04:00
let s:run_jobs = (exists('*ch_close_in') || exists('*jobstart')) && exists('*bufwinid')
2021-10-11 05:30:43 -04:00
2021-05-31 16:30:26 -04:00
function! s:GitCmd() abort
if !exists('g:fugitive_git_executable')
return ['git']
elseif type(g:fugitive_git_executable) == type([])
return g:fugitive_git_executable
else
let dquote = '"\%([^"]\|""\|\\"\)*"\|'
let string = g:fugitive_git_executable
let list = []
if string =~# '^\w\+='
2021-07-30 16:52:54 -04:00
call add(list, '/usr/bin/env')
2021-05-31 16:30:26 -04:00
endif
while string =~# '\S'
2022-08-08 09:45:56 -04:00
let arg = matchstr(string, '^\s*\%(' . dquote . '''[^'']*''\|\\.\|[^' . "\t" . ' |]\)\+')
2021-05-31 16:30:26 -04:00
let string = strpart(string, len(arg))
let arg = substitute(arg, '^\s\+', '', '')
let arg = substitute(arg,
2022-11-20 06:21:23 -05:00
\ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\)\|' . s:expand,
2021-05-31 16:30:26 -04:00
\ '\=submatch(0)[0] ==# "\\" ? submatch(0)[1] : submatch(0)[1:-2]', 'g')
call add(list, arg)
endwhile
return list
endif
endfunction
function! s:GitShellCmd() abort
if !exists('g:fugitive_git_executable')
return 'git'
elseif type(g:fugitive_git_executable) == type([])
return s:shellesc(g:fugitive_git_executable)
else
return g:fugitive_git_executable
endif
endfunction
2021-05-05 04:25:00 -04:00
function! s:UserCommandCwd(dir) abort
let tree = s:Tree(a:dir)
2022-08-08 09:45:56 -04:00
return len(tree) ? s:VimSlash(tree) : getcwd()
2021-05-05 04:25:00 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:UserCommandList(...) abort
2021-05-05 04:25:00 -04:00
if !fugitive#GitVersion(1, 8, 5)
throw 'fugitive: Git 1.8.5 or higher required'
endif
2021-05-31 16:30:26 -04:00
if !exists('g:fugitive_git_command')
let git = s:GitCmd()
elseif type(g:fugitive_git_command) == type([])
let git = g:fugitive_git_command
else
let git = split(g:fugitive_git_command, '\s\+')
endif
2020-04-25 21:56:16 -04:00
let flags = []
if a:0 && type(a:1) == type({})
let git = copy(get(a:1, 'git', git))
let flags = get(a:1, 'flags', flags)
2021-10-11 05:30:43 -04:00
let dir = a:1.git_dir
2020-04-25 21:56:16 -04:00
elseif a:0
2021-10-11 05:30:43 -04:00
let dir = s:GitDir(a:1)
2020-04-25 21:56:16 -04:00
else
let dir = ''
endif
2019-08-22 11:36:17 -04:00
if len(dir)
let tree = s:Tree(dir)
if empty(tree)
call add(git, '--git-dir=' . FugitiveGitPath(dir))
2021-10-11 05:30:43 -04:00
else
if !s:cpath(tree . '/.git', dir) || len($GIT_DIR)
call add(git, '--git-dir=' . FugitiveGitPath(dir))
endif
if !s:cpath(tree, getcwd())
call extend(git, ['-C', FugitiveGitPath(tree)])
endif
2019-08-22 11:36:17 -04:00
endif
endif
2020-04-25 21:56:16 -04:00
return git + flags
2018-08-25 12:13:42 -04:00
endfunction
2018-07-19 08:52:53 -04:00
let s:git_versions = {}
2018-06-14 06:31:12 -04:00
function! fugitive#GitVersion(...) abort
2021-05-31 16:30:26 -04:00
let git = s:GitShellCmd()
if !has_key(s:git_versions, git)
2021-10-11 05:30:43 -04:00
let s:git_versions[git] = matchstr(get(s:JobExecute(s:GitCmd() + ['--version'], {}, [], [], {}).stdout, 0, ''), '\d[^[:space:]]\+')
2018-06-14 06:31:12 -04:00
endif
2019-03-08 06:04:56 -05:00
if !a:0
2021-05-31 16:30:26 -04:00
return s:git_versions[git]
2019-03-08 06:04:56 -05:00
endif
2021-05-31 16:30:26 -04:00
let components = split(s:git_versions[git], '\D\+')
2019-03-08 06:04:56 -05:00
if empty(components)
return -1
endif
for i in range(len(a:000))
if a:000[i] > +get(components, i)
return 0
elseif a:000[i] < +get(components, i)
return 1
endif
endfor
return a:000[i] ==# get(components, i)
2018-06-14 06:31:12 -04:00
endfunction
2019-03-08 06:04:56 -05:00
function! s:Dir(...) abort
return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
endfunction
2021-10-11 05:30:43 -04:00
function! s:GitDir(...) abort
return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
endfunction
2022-08-08 09:45:56 -04:00
function! s:InitializeBuffer(repo) abort
let b:git_dir = s:GitDir(a:repo)
2021-10-11 05:30:43 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:SameRepo(one, two) abort
let one = s:GitDir(a:one)
return !empty(one) && one ==# s:GitDir(a:two)
endfunction
if exists('+shellslash')
function! s:DirUrlPrefix(dir) abort
let gd = s:GitDir(a:dir)
return 'fugitive://' . (gd =~# '^[^/]' ? '/' : '') . s:PathUrlEncode(gd) . '//'
endfunction
else
function! s:DirUrlPrefix(dir) abort
return 'fugitive://' . s:PathUrlEncode(s:GitDir(a:dir)) . '//'
endfunction
endif
2018-07-30 17:18:16 -04:00
function! s:Tree(...) abort
2019-03-08 06:04:56 -05:00
return a:0 ? FugitiveWorkTree(a:1) : FugitiveWorkTree()
2018-07-30 17:18:16 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:HasOpt(args, ...) abort
let args = a:args[0 : index(a:args, '--')]
let opts = copy(a:000)
if type(opts[0]) == type([])
if empty(args) || index(opts[0], args[0]) == -1
return 0
endif
call remove(opts, 0)
endif
for opt in opts
if index(args, opt) != -1
return 1
endif
endfor
endfunction
2021-10-11 05:30:43 -04:00
function! s:PreparePathArgs(cmd, dir, literal, explicit) abort
if !a:explicit
2018-08-25 12:13:42 -04:00
call insert(a:cmd, '--literal-pathspecs')
endif
let split = index(a:cmd, '--')
2019-08-22 11:36:17 -04:00
for i in range(split < 0 ? len(a:cmd) : split)
2021-10-11 05:30:43 -04:00
if type(a:cmd[i]) == type(0)
if a:literal
let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
else
let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), ':(top,literal)', a:dir)
endif
endif
2019-08-22 11:36:17 -04:00
endfor
2018-09-24 20:40:17 -04:00
if split < 0
2018-08-25 12:13:42 -04:00
return a:cmd
endif
for i in range(split + 1, len(a:cmd) - 1)
2018-09-24 20:40:17 -04:00
if type(a:cmd[i]) == type(0)
2021-10-11 05:30:43 -04:00
if a:literal
let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
else
let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), ':(top,literal)', a:dir)
endif
elseif !a:explicit
2018-09-24 20:40:17 -04:00
let a:cmd[i] = fugitive#Path(a:cmd[i], './', a:dir)
endif
2018-08-25 12:13:42 -04:00
endfor
return a:cmd
endfunction
2022-08-08 09:45:56 -04:00
let s:git_index_file_env = {}
function! s:GitIndexFileEnv() abort
if $GIT_INDEX_FILE =~# '^/\|^\a:' && !has_key(s:git_index_file_env, $GIT_INDEX_FILE)
let s:git_index_file_env[$GIT_INDEX_FILE] = s:Slash(FugitiveVimPath($GIT_INDEX_FILE))
endif
return get(s:git_index_file_env, $GIT_INDEX_FILE, '')
endfunction
2021-10-11 05:30:43 -04:00
function! s:PrepareEnv(env, dir) abort
if len($GIT_INDEX_FILE) && len(s:Tree(a:dir)) && !has_key(a:env, 'GIT_INDEX_FILE')
2022-08-08 09:45:56 -04:00
let index_dir = substitute(s:GitIndexFileEnv(), '[^/]\+$', '', '')
2021-10-11 05:30:43 -04:00
let our_dir = fugitive#Find('.git/', a:dir)
2022-08-08 09:45:56 -04:00
if !s:cpath(index_dir, our_dir) && !s:cpath(resolve(index_dir), our_dir)
2021-10-11 05:30:43 -04:00
let a:env['GIT_INDEX_FILE'] = FugitiveGitPath(fugitive#Find('.git/index', a:dir))
endif
endif
if len($GIT_WORK_TREE)
let a:env['GIT_WORK_TREE'] = '.'
endif
endfunction
2018-09-24 20:40:17 -04:00
let s:prepare_env = {
\ 'sequence.editor': 'GIT_SEQUENCE_EDITOR',
\ 'core.editor': 'GIT_EDITOR',
\ 'core.askpass': 'GIT_ASKPASS',
\ }
2021-10-11 05:30:43 -04:00
function! fugitive#PrepareDirEnvGitFlagsArgs(...) abort
2021-05-05 04:25:00 -04:00
if !fugitive#GitVersion(1, 8, 5)
throw 'fugitive: Git 1.8.5 or higher required'
endif
2021-05-31 16:30:26 -04:00
let git = s:GitCmd()
2022-08-08 09:45:56 -04:00
if a:0 == 1 && type(a:1) == type({}) && (has_key(a:1, 'fugitive_dir') || has_key(a:1, 'git_dir')) && has_key(a:1, 'flags') && has_key(a:1, 'args')
2021-10-11 05:30:43 -04:00
let cmd = a:1.flags + a:1.args
2022-08-08 09:45:56 -04:00
let dir = s:Dir(a:1)
2021-10-11 05:30:43 -04:00
if has_key(a:1, 'git')
let git = a:1.git
endif
let env = get(a:1, 'env', {})
2018-09-24 20:40:17 -04:00
else
2021-10-11 05:30:43 -04:00
let list_args = []
let cmd = []
2022-08-08 09:45:56 -04:00
for l:.arg in a:000
2021-10-11 05:30:43 -04:00
if type(arg) ==# type([])
call extend(list_args, arg)
else
call add(cmd, arg)
endif
endfor
call extend(cmd, list_args)
let env = {}
2018-09-24 20:40:17 -04:00
endif
2021-10-11 05:30:43 -04:00
let autoenv = {}
let explicit_pathspec_option = 0
let literal_pathspecs = 1
2018-09-24 20:40:17 -04:00
let i = 0
2021-10-11 05:30:43 -04:00
let arg_count = 0
2018-09-24 20:40:17 -04:00
while i < len(cmd)
2021-05-05 04:25:00 -04:00
if type(cmd[i]) == type({})
2022-08-08 09:45:56 -04:00
if has_key(cmd[i], 'fugitive_dir') || has_key(cmd[i], 'git_dir')
let dir = s:Dir(cmd[i])
2021-05-05 04:25:00 -04:00
endif
if has_key(cmd[i], 'git')
let git = cmd[i].git
endif
2021-10-11 05:30:43 -04:00
if has_key(cmd[i], 'env')
call extend(env, cmd[i].env)
endif
2021-05-05 04:25:00 -04:00
call remove(cmd, i)
elseif cmd[i] =~# '^$\|[\/.]' && cmd[i] !~# '^-'
2022-08-08 09:45:56 -04:00
let dir = s:Dir(remove(cmd, i))
2019-08-22 11:36:17 -04:00
elseif cmd[i] =~# '^--git-dir='
2022-08-08 09:45:56 -04:00
let dir = s:Dir(remove(cmd, i)[10:-1])
2018-09-24 20:40:17 -04:00
elseif type(cmd[i]) ==# type(0)
2019-03-08 06:04:56 -05:00
let dir = s:Dir(remove(cmd, i))
2018-09-24 20:40:17 -04:00
elseif cmd[i] ==# '-c' && len(cmd) > i + 1
let key = matchstr(cmd[i+1], '^[^=]*')
2021-10-11 05:30:43 -04:00
if has_key(s:prepare_env, tolower(key))
let var = s:prepare_env[tolower(key)]
2018-09-24 20:40:17 -04:00
let val = matchstr(cmd[i+1], '=\zs.*')
2021-10-11 05:30:43 -04:00
let autoenv[var] = val
2018-09-24 20:40:17 -04:00
endif
2021-10-11 05:30:43 -04:00
let i += 2
2018-09-24 20:40:17 -04:00
elseif cmd[i] =~# '^--.*pathspecs$'
2021-10-11 05:30:43 -04:00
let literal_pathspecs = (cmd[i] ==# '--literal-pathspecs')
2018-09-24 20:40:17 -04:00
let explicit_pathspec_option = 1
2021-05-05 04:25:00 -04:00
let i += 1
2018-09-24 20:40:17 -04:00
elseif cmd[i] !~# '^-'
2021-10-11 05:30:43 -04:00
let arg_count = len(cmd) - i
2018-09-24 20:40:17 -04:00
break
2018-07-30 17:18:16 -04:00
else
2018-09-24 20:40:17 -04:00
let i += 1
2018-07-30 17:18:16 -04:00
endif
2018-09-24 20:40:17 -04:00
endwhile
if !exists('dir')
2019-03-08 06:04:56 -05:00
let dir = s:Dir()
2018-07-30 17:18:16 -04:00
endif
2021-10-11 05:30:43 -04:00
call extend(autoenv, env)
call s:PrepareEnv(autoenv, dir)
if len($GPG_TTY) && !has_key(autoenv, 'GPG_TTY')
let autoenv.GPG_TTY = ''
endif
call s:PreparePathArgs(cmd, dir, literal_pathspecs, explicit_pathspec_option)
2022-08-08 09:45:56 -04:00
return [dir, env, extend(autoenv, env), git, cmd[0 : -arg_count-1], arg_count ? cmd[-arg_count : -1] : []]
2019-08-22 11:36:17 -04:00
endfunction
2020-01-28 21:07:36 -05:00
function! s:BuildEnvPrefix(env) abort
let pre = ''
let env = items(a:env)
if empty(env)
return ''
2021-10-11 05:30:43 -04:00
elseif &shell =~# '\%(powershell\|pwsh\)\%(\.exe\)\=$'
2020-01-28 21:07:36 -05:00
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
2021-07-30 16:52:54 -04:00
return '/usr/bin/env ' . s:shellesc(map(env, 'join(v:val, "=")')) . ' '
2020-01-28 21:07:36 -05:00
endif
endfunction
2020-04-25 21:56:16 -04:00
function! s:JobOpts(cmd, env) abort
if empty(a:env)
return [a:cmd, {}]
2021-10-11 05:30:43 -04:00
elseif has('patch-8.2.0239') ||
\ has('nvim') && api_info().version.api_level - api_info().version.api_prerelease >= 7 ||
\ has('patch-8.0.0902') && !has('nvim') && (!has('win32') || empty(filter(keys(a:env), 'exists("$" . v:val)')))
2020-04-25 21:56:16 -04:00
return [a:cmd, {'env': a:env}]
endif
let envlist = map(items(a:env), 'join(v:val, "=")')
if !has('win32')
2021-07-30 16:52:54 -04:00
return [['/usr/bin/env'] + envlist + a:cmd, {}]
2020-04-25 21:56:16 -04:00
else
let pre = join(map(envlist, '"set " . substitute(v:val, "[&|<>^]", "^^^&", "g") . "& "'), '')
if len(a:cmd) == 3 && a:cmd[0] ==# 'cmd.exe' && a:cmd[1] ==# '/c'
return [a:cmd[0:1] + [pre . a:cmd[2]], {}]
else
return [['cmd.exe', '/c', pre . s:WinShellEsc(a:cmd)], {}]
endif
endif
endfunction
2021-10-11 05:30:43 -04:00
function! s:PrepareJob(opts) abort
let dict = {'argv': a:opts.argv}
if has_key(a:opts, 'env')
let dict.env = a:opts.env
endif
let [argv, jopts] = s:JobOpts(a:opts.argv, get(a:opts, 'env', {}))
if has_key(a:opts, 'cwd')
if has('patch-8.0.0902')
let jopts.cwd = a:opts.cwd
let dict.cwd = a:opts.cwd
else
throw 'fugitive: cwd unsupported'
endif
endif
return [argv, jopts, dict]
endfunction
function! fugitive#PrepareJob(...) abort
if a:0 == 1 && type(a:1) == type({}) && has_key(a:1, 'argv') && !has_key(a:1, 'args')
return s:PrepareJob(a:1)
endif
2022-08-08 09:45:56 -04:00
let [repo, user_env, exec_env, git, flags, args] = call('fugitive#PrepareDirEnvGitFlagsArgs', a:000)
let dir = s:GitDir(repo)
2021-10-11 05:30:43 -04:00
let dict = {'git': git, 'git_dir': dir, 'flags': flags, 'args': args}
if len(user_env)
let dict.env = user_env
endif
let cmd = flags + args
2022-08-08 09:45:56 -04:00
let tree = s:Tree(repo)
2021-10-11 05:30:43 -04:00
if empty(tree) || index(cmd, '--') == len(cmd) - 1
let dict.cwd = getcwd()
call extend(cmd, ['--git-dir=' . FugitiveGitPath(dir)], 'keep')
else
2022-08-08 09:45:56 -04:00
let dict.cwd = s:VimSlash(tree)
2021-10-11 05:30:43 -04:00
call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep')
if !s:cpath(tree . '/.git', dir) || len($GIT_DIR)
call extend(cmd, ['--git-dir=' . FugitiveGitPath(dir)], 'keep')
endif
endif
call extend(cmd, git, 'keep')
return s:JobOpts(cmd, exec_env) + [dict]
endfunction
function! fugitive#Execute(...) abort
let cb = copy(a:000)
let cmd = []
let stdin = []
while len(cb) && type(cb[0]) !=# type(function('tr'))
if type(cb[0]) ==# type({}) && has_key(cb[0], 'stdin')
if type(cb[0].stdin) == type([])
call extend(stdin, cb[0].stdin)
elseif type(cb[0].stdin) == type('')
call extend(stdin, readfile(cb[0].stdin, 'b'))
endif
if len(keys(cb[0])) == 1
call remove(cb, 0)
continue
endif
endif
call add(cmd, remove(cb, 0))
endwhile
let [argv, jopts, dict] = call('fugitive#PrepareJob', cmd)
return s:JobExecute(argv, jopts, stdin, cb, dict)
endfunction
2021-05-05 04:25:00 -04:00
function! s:BuildShell(dir, env, git, args) abort
2019-08-22 11:36:17 -04:00
let cmd = copy(a:args)
let tree = s:Tree(a:dir)
2020-01-28 21:07:36 -05:00
let pre = s:BuildEnvPrefix(a:env)
2018-09-24 20:40:17 -04:00
if empty(tree) || index(cmd, '--') == len(cmd) - 1
2019-08-22 11:36:17 -04:00
call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
2019-03-08 06:04:56 -05:00
else
2021-05-05 04:25:00 -04:00
call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep')
2021-10-11 05:30:43 -04:00
if !s:cpath(tree . '/.git', a:dir) || len($GIT_DIR)
call extend(cmd, ['--git-dir=' . FugitiveGitPath(a:dir)], 'keep')
endif
2018-08-25 12:13:42 -04:00
endif
2021-05-05 04:25:00 -04:00
return pre . join(map(a:git + cmd, 's:shellesc(v:val)'))
2019-08-22 11:36:17 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:JobNvimCallback(lines, job, data, type) abort
let a:lines[-1] .= remove(a:data, 0)
call extend(a:lines, a:data)
endfunction
function! s:SystemList(cmd) abort
let exit = []
if exists('*jobstart')
let lines = ['']
let jopts = {
\ 'on_stdout': function('s:JobNvimCallback', [lines]),
\ 'on_stderr': function('s:JobNvimCallback', [lines]),
\ 'on_exit': { j, code, _ -> add(exit, code) }}
let job = jobstart(a:cmd, jopts)
call chanclose(job, 'stdin')
call jobwait([job])
if empty(lines[-1])
call remove(lines, -1)
endif
return [lines, exit[0]]
2022-08-08 09:45:56 -04:00
elseif exists('*ch_close_in')
2021-10-11 05:30:43 -04:00
let lines = []
let jopts = {
\ 'out_cb': { j, str -> add(lines, str) },
\ 'err_cb': { j, str -> add(lines, str) },
\ 'exit_cb': { j, code -> add(exit, code) }}
let job = job_start(a:cmd, jopts)
call ch_close_in(job)
let sleep = has('patch-8.2.2366') ? 'sleep! 1m' : 'sleep 1m'
2022-08-08 09:45:56 -04:00
while ch_status(job) !~# '^closed$\|^fail$' || job_status(job) ==# 'run'
2021-10-11 05:30:43 -04:00
exe sleep
endwhile
return [lines, exit[0]]
else
let [output, exec_error] = s:SystemError(s:shellesc(a:cmd))
let lines = split(output, "\n", 1)
if empty(lines[-1])
call remove(lines, -1)
endif
return [lines, v:shell_error]
endif
endfunction
function! fugitive#ShellCommand(...) abort
2022-08-08 09:45:56 -04:00
let [repo, _, env, git, flags, args] = call('fugitive#PrepareDirEnvGitFlagsArgs', a:000)
return s:BuildShell(s:GitDir(repo), env, git, flags + args)
2019-08-22 11:36:17 -04:00
endfunction
function! s:SystemError(cmd, ...) abort
2021-10-11 05:30:43 -04:00
let cmd = type(a:cmd) == type([]) ? s:shellesc(a:cmd) : a:cmd
2019-08-22 11:36:17 -04:00
try
if &shellredir ==# '>' && &shell =~# 'sh\|cmd'
let shellredir = &shellredir
if &shell =~# 'csh'
set shellredir=>&
else
set shellredir=>%s\ 2>&1
endif
endif
2021-05-05 04:25:00 -04:00
if exists('+guioptions') && &guioptions =~# '!'
let guioptions = &guioptions
set guioptions-=!
endif
2021-10-11 05:30:43 -04:00
let out = call('system', [cmd] + a:000)
2019-08-22 11:36:17 -04:00
return [out, v:shell_error]
catch /^Vim\%((\a\+)\)\=:E484:/
let opts = ['shell', 'shellcmdflag', 'shellredir', 'shellquote', 'shellxquote', 'shellxescape', 'shellslash']
call filter(opts, 'exists("+".v:val) && !empty(eval("&".v:val))')
call map(opts, 'v:val."=".eval("&".v:val)')
2021-10-11 05:30:43 -04:00
call s:throw('failed to run `' . cmd . '` with ' . join(opts, ' '))
2019-08-22 11:36:17 -04:00
finally
if exists('shellredir')
let &shellredir = shellredir
endif
2021-05-05 04:25:00 -04:00
if exists('guioptions')
let &guioptions = guioptions
endif
2019-08-22 11:36:17 -04:00
endtry
endfunction
2021-10-11 05:30:43 -04:00
function! s:ChompStderr(...) abort
let r = call('fugitive#Execute', a:000)
return !r.exit_status ? '' : len(r.stderr) > 1 ? s:JoinChomp(r.stderr) : 'unknown Git error' . string(r)
2019-08-22 11:36:17 -04:00
endfunction
function! s:ChompDefault(default, ...) abort
2021-10-11 05:30:43 -04:00
let r = call('fugitive#Execute', a:000)
return r.exit_status ? a:default : s:JoinChomp(r.stdout)
2019-08-22 11:36:17 -04:00
endfunction
function! s:LinesError(...) abort
2021-10-11 05:30:43 -04:00
let r = call('fugitive#Execute', a:000)
if empty(r.stdout[-1])
call remove(r.stdout, -1)
2021-07-04 16:47:44 -04:00
endif
2021-10-11 05:30:43 -04:00
return [r.exit_status ? [] : r.stdout, r.exit_status]
2018-08-25 12:13:42 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:NullError(cmd) abort
let r = fugitive#Execute(a:cmd)
let list = r.exit_status ? [] : split(tr(join(r.stdout, "\1"), "\1\n", "\n\1"), "\1", 1)[0:-2]
return [list, s:JoinChomp(r.stderr), r.exit_status]
2019-08-22 11:36:17 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:TreeChomp(...) abort
let r = call('fugitive#Execute', a:000)
if !r.exit_status
return s:JoinChomp(r.stdout)
endif
throw 'fugitive: error running `' . call('fugitive#ShellCommand', a:000) . '`: ' . s:JoinChomp(r.stderr)
endfunction
function! s:StdoutToFile(out, cmd, ...) abort
let [argv, jopts, _] = fugitive#PrepareJob(a:cmd)
let exit = []
if exists('*jobstart')
call extend(jopts, {
\ 'stdout_buffered': v:true,
\ 'stderr_buffered': v:true,
\ 'on_exit': { j, code, _ -> add(exit, code) }})
let job = jobstart(argv, jopts)
if a:0
call chansend(job, a:1)
endif
call chanclose(job, 'stdin')
call jobwait([job])
if len(a:out)
call writefile(jopts.stdout, a:out, 'b')
endif
return [join(jopts.stderr, "\n"), exit[0]]
2022-08-08 09:45:56 -04:00
elseif exists('*ch_close_in')
2021-10-11 05:30:43 -04:00
try
let err = tempname()
call extend(jopts, {
\ 'out_io': len(a:out) ? 'file' : 'null',
\ 'out_name': a:out,
\ 'err_io': 'file',
\ 'err_name': err,
\ 'exit_cb': { j, code -> add(exit, code) }})
let job = job_start(argv, jopts)
if a:0
call ch_sendraw(job, a:1)
endif
call ch_close_in(job)
2022-08-08 09:45:56 -04:00
while ch_status(job) !~# '^closed$\|^fail$' || job_status(job) ==# 'run'
2021-10-11 05:30:43 -04:00
exe has('patch-8.2.2366') ? 'sleep! 1m' : 'sleep 1m'
endwhile
return [join(readfile(err, 'b'), "\n"), exit[0]]
finally
call delete(err)
endtry
elseif s:winshell() || &shell !~# 'sh' || &shell =~# 'fish\|\%(powershell\|pwsh\)\%(\.exe\)\=$'
throw 'fugitive: Vim 8 or higher required to use ' . &shell
2020-04-25 21:56:16 -04:00
else
2021-10-11 05:30:43 -04:00
let cmd = fugitive#ShellCommand(a:cmd)
return s:SystemError(' (' . cmd . ' >' . a:out . ') ')
2020-04-25 21:56:16 -04:00
endif
2018-09-24 20:40:17 -04:00
endfunction
2019-11-16 10:28:42 -05:00
let s:head_cache = {}
2018-08-25 12:13:42 -04:00
function! fugitive#Head(...) abort
2019-03-08 06:04:56 -05:00
let dir = a:0 > 1 ? a:2 : s:Dir()
2019-11-16 10:28:42 -05:00
if empty(dir)
return ''
endif
2022-08-08 09:45:56 -04:00
let file = FugitiveActualDir() . '/HEAD'
2019-11-16 10:28:42 -05:00
let ftime = getftime(file)
if ftime == -1
2018-08-25 12:13:42 -04:00
return ''
2021-10-11 05:30:43 -04:00
elseif ftime != get(s:head_cache, file, [-1])[0]
let s:head_cache[file] = [ftime, readfile(file)[0]]
2018-08-25 12:13:42 -04:00
endif
2021-10-11 05:30:43 -04:00
let head = s:head_cache[file][1]
let len = a:0 ? a:1 : 0
2018-08-25 12:13:42 -04:00
if head =~# '^ref: '
2021-10-11 05:30:43 -04:00
if len < 0
return strpart(head, 5)
else
return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
endif
2019-08-22 11:36:17 -04:00
elseif head =~# '^\x\{40,\}$'
2021-10-11 05:30:43 -04:00
return len < 0 ? head : strpart(head, 0, len)
2018-08-25 12:13:42 -04:00
else
return ''
endif
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#RevParse(rev, ...) abort
2021-10-11 05:30:43 -04:00
let hash = s:ChompDefault('', [a:0 ? a:1 : s:Dir(), 'rev-parse', '--verify', a:rev, '--'])
if hash =~# '^\x\{40,\}$'
2018-07-30 17:18:16 -04:00
return hash
endif
2021-10-11 05:30:43 -04:00
throw 'fugitive: failed to parse revision ' . a:rev
2018-07-30 17:18:16 -04:00
endfunction
2021-10-11 05:30:43 -04:00
" Section: Git config
2019-01-08 05:11:54 -05:00
function! s:ConfigTimestamps(dir, dict) abort
let files = ['/etc/gitconfig', '~/.gitconfig',
\ len($XDG_CONFIG_HOME) ? $XDG_CONFIG_HOME . '/git/config' : '~/.config/git/config']
if len(a:dir)
call add(files, fugitive#Find('.git/config', a:dir))
endif
call extend(files, get(a:dict, 'include.path', []))
return join(map(files, 'getftime(expand(v:val))'), ',')
endfunction
2021-10-11 05:30:43 -04:00
function! s:ConfigCallback(r, into) abort
let dict = a:into[1]
if has_key(dict, 'job')
call remove(dict, 'job')
endif
let lines = a:r.exit_status ? [] : split(tr(join(a:r.stdout, "\1"), "\1\n", "\n\1"), "\1", 1)[0:-2]
for line in lines
let key = matchstr(line, "^[^\n]*")
if !has_key(dict, key)
let dict[key] = []
endif
if len(key) ==# len(line)
call add(dict[key], 1)
else
call add(dict[key], strpart(line, len(key) + 1))
endif
endfor
let callbacks = remove(dict, 'callbacks')
lockvar! dict
let a:into[0] = s:ConfigTimestamps(dict.git_dir, dict)
for callback in callbacks
call call(callback[0], [dict] + callback[1:-1])
endfor
endfunction
2021-07-30 16:52:54 -04:00
let s:config_prototype = {}
2019-01-08 05:11:54 -05:00
let s:config = {}
2021-10-11 05:30:43 -04:00
function! fugitive#ExpireConfig(...) abort
if !a:0 || a:1 is# 0
let s:config = {}
else
let key = a:1 is# '' ? '_' : s:GitDir(a:0 ? a:1 : -1)
if len(key) && has_key(s:config, key)
call remove(s:config, key)
endif
endif
endfunction
2019-01-08 05:11:54 -05:00
function! fugitive#Config(...) abort
let name = ''
2021-05-31 16:30:26 -04:00
let default = get(a:, 3, '')
2021-10-11 05:30:43 -04:00
if a:0 && type(a:1) == type(function('tr'))
let dir = s:Dir()
let callback = a:000
elseif a:0 > 1 && type(a:2) == type(function('tr'))
if type(a:1) == type({}) && has_key(a:1, 'GetAll')
if has_key(a:1, 'callbacks')
call add(a:1.callbacks, a:000[1:-1])
else
call call(a:2, [a:1] + a:000[2:-1])
endif
return a:1
else
let dir = s:Dir(a:1)
let callback = a:000[1:-1]
endif
elseif a:0 >= 2 && type(a:2) == type({}) && has_key(a:2, 'GetAll')
return get(fugitive#ConfigGetAll(a:1, a:2), 0, default)
2019-01-08 05:11:54 -05:00
elseif a:0 >= 2
2021-07-04 16:47:44 -04:00
let dir = s:Dir(a:2)
2019-01-08 05:11:54 -05:00
let name = a:1
2021-10-11 05:30:43 -04:00
elseif a:0 == 1 && type(a:1) == type({}) && has_key(a:1, 'GetAll')
2019-01-08 05:11:54 -05:00
return a:1
2021-07-04 16:47:44 -04:00
elseif a:0 == 1 && type(a:1) == type('') && a:1 =~# '^[[:alnum:]-]\+\.'
let dir = s:Dir()
2019-01-08 05:11:54 -05:00
let name = a:1
elseif a:0 == 1
2021-07-04 16:47:44 -04:00
let dir = s:Dir(a:1)
else
let dir = s:Dir()
2019-01-08 05:11:54 -05:00
endif
let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
2021-10-11 05:30:43 -04:00
let git_dir = s:GitDir(dir)
let dir_key = len(git_dir) ? git_dir : '_'
let [ts, dict] = get(s:config, dir_key, ['new', {}])
if !has_key(dict, 'job') && ts !=# s:ConfigTimestamps(git_dir, dict)
2021-07-30 16:52:54 -04:00
let dict = copy(s:config_prototype)
2021-10-11 05:30:43 -04:00
let dict.git_dir = git_dir
let into = ['running', dict]
let dict.callbacks = []
let exec = fugitive#Execute([dir, 'config', '--list', '-z', '--'], function('s:ConfigCallback'), into)
if has_key(exec, 'job')
let dict.job = exec.job
endif
let s:config[dir_key] = into
endif
if !exists('l:callback')
call fugitive#Wait(dict)
elseif has_key(dict, 'callbacks')
call add(dict.callbacks, callback)
else
call call(callback[0], [dict] + callback[1:-1])
2019-01-08 05:11:54 -05:00
endif
2021-10-11 05:30:43 -04:00
return len(name) ? get(fugitive#ConfigGetAll(name, dict), 0, default) : dict
2018-07-30 17:18:16 -04:00
endfunction
2021-07-04 16:47:44 -04:00
function! fugitive#ConfigGetAll(name, ...) abort
2021-10-11 05:30:43 -04:00
if a:0 && (type(a:name) !=# type('') || a:name !~# '^[[:alnum:]-]\+\.' && type(a:1) ==# type('') && a:1 =~# '^[[:alnum:]-]\+\.')
let config = fugitive#Config(a:name)
let name = a:1
else
let config = fugitive#Config(a:0 ? a:1 : s:Dir())
let name = a:name
endif
let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
call fugitive#Wait(config)
return name =~# '\.' ? copy(get(config, name, [])) : []
2021-07-04 16:47:44 -04:00
endfunction
function! fugitive#ConfigGetRegexp(pattern, ...) abort
2021-10-11 05:30:43 -04:00
if type(a:pattern) !=# type('')
let config = fugitive#Config(a:name)
let pattern = a:0 ? a:1 : '.*'
else
let config = fugitive#Config(a:0 ? a:1 : s:Dir())
let pattern = a:pattern
endif
call fugitive#Wait(config)
let filtered = map(filter(copy(config), 'v:key =~# "\\." && v:key =~# pattern'), 'copy(v:val)')
if pattern !~# '\\\@<!\%(\\\\\)*\\z[se]'
2021-07-04 16:47:44 -04:00
return filtered
endif
let transformed = {}
for [k, v] in items(filtered)
2021-10-11 05:30:43 -04:00
let k = matchstr(k, pattern)
2021-07-04 16:47:44 -04:00
if len(k)
let transformed[k] = v
endif
endfor
return transformed
endfunction
2021-07-30 16:52:54 -04:00
function! s:config_GetAll(name) dict abort
let name = substitute(a:name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
2021-10-11 05:30:43 -04:00
call fugitive#Wait(self)
return name =~# '\.' ? copy(get(self, name, [])) : []
2021-07-30 16:52:54 -04:00
endfunction
function! s:config_Get(name, ...) dict abort
return get(self.GetAll(a:name), 0, a:0 ? a:1 : '')
endfunction
function! s:config_GetRegexp(pattern) dict abort
return fugitive#ConfigGetRegexp(self, a:pattern)
endfunction
call s:add_methods('config', ['GetAll', 'Get', 'GetRegexp'])
2021-10-11 05:30:43 -04:00
function! s:RemoteDefault(dir) abort
2018-07-30 17:18:16 -04:00
let head = FugitiveHead(0, a:dir)
2021-05-31 16:30:26 -04:00
let remote = len(head) ? FugitiveConfigGet('branch.' . head . '.remote', a:dir) : ''
2018-07-30 17:18:16 -04:00
let i = 10
while remote ==# '.' && i > 0
2021-05-31 16:30:26 -04:00
let head = matchstr(FugitiveConfigGet('branch.' . head . '.merge', a:dir), 'refs/heads/\zs.*')
let remote = len(head) ? FugitiveConfigGet('branch.' . head . '.remote', a:dir) : ''
2018-07-30 17:18:16 -04:00
let i -= 1
endwhile
2018-08-25 12:13:42 -04:00
return remote =~# '^\.\=$' ? 'origin' : remote
2018-07-30 17:18:16 -04:00
endfunction
2021-07-30 16:52:54 -04:00
function! s:SshParseHost(value) abort
let patterns = []
let negates = []
for host in split(a:value, '\s\+')
let pattern = substitute(host, '[\\^$.*~?]', '\=submatch(0) == "*" ? ".*" : submatch(0) == "?" ? "." : "\\" . submatch(0)', 'g')
if pattern[0] ==# '!'
call add(negates, '\&\%(^' . pattern[1 : -1] . '$\)\@!')
else
call add(patterns, pattern)
2021-05-05 04:25:00 -04:00
endif
2021-07-30 16:52:54 -04:00
endfor
return '^\%(' . join(patterns, '\|') . '\)$' . join(negates, '')
endfunction
function! s:SshParseConfig(into, root, file, ...) abort
if !filereadable(a:file)
return a:into
endif
let host = a:0 ? a:1 : '^\%(.*\)$'
for line in readfile(a:file)
let key = tolower(matchstr(line, '^\s*\zs\w\+\ze\s'))
let value = matchstr(line, '^\s*\w\+\s\+\zs.*\S')
if key ==# 'match'
let host = value ==# 'all' ? '^\%(.*\)$' : ''
elseif key ==# 'host'
let host = s:SshParseHost(value)
elseif key ==# 'include'
call s:SshParseInclude(a:into, a:root, host, value)
elseif len(key) && len(host)
2022-08-08 09:45:56 -04:00
call extend(a:into, {key : []}, 'keep')
2021-07-30 16:52:54 -04:00
call add(a:into[key], [host, value])
endif
endfor
return a:into
endfunction
function! s:SshParseInclude(into, root, host, value) abort
for glob in split(a:value)
if glob !~# '^/'
let glob = a:root . glob
endif
for file in split(glob(glob), "\n")
call s:SshParseConfig(a:into, a:root, file, a:host)
endfor
endfor
endfunction
unlet! s:ssh_config
function! fugitive#SshConfig(host, ...) abort
if !exists('s:ssh_config')
let s:ssh_config = {}
for file in [expand("~/.ssh/config"), "/etc/ssh/ssh_config"]
call s:SshParseConfig(s:ssh_config, substitute(file, '\w*$', '', ''), file)
endfor
2021-05-05 04:25:00 -04:00
endif
2021-07-30 16:52:54 -04:00
let host_config = {}
for key in a:0 ? a:1 : keys(s:ssh_config)
for [host_pattern, value] in get(s:ssh_config, key, [])
if a:host =~# host_pattern
let host_config[key] = value
break
endif
endfor
endfor
return host_config
endfunction
function! fugitive#SshHostAlias(authority) abort
let [_, user, host, port; __] = matchlist(a:authority, '^\%(\([^/@]\+\)@\)\=\(.\{-\}\)\%(:\(\d\+\)\)\=$')
let c = fugitive#SshConfig(host, ['user', 'hostname', 'port'])
if empty(user)
let user = get(c, 'user', '')
endif
if empty(port)
let port = get(c, 'port', '')
2021-05-05 04:25:00 -04:00
endif
2021-07-30 16:52:54 -04:00
return (len(user) ? user . '@' : '') . get(c, 'hostname', host) . (port =~# '^\%(22\)\=$' ? '' : ':' . port)
2021-05-05 04:25:00 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:CurlResponse(result) abort
let a:result.headers = {}
for line in a:result.exit_status ? [] : remove(a:result, 'stdout')
let header = matchlist(line, '^\([[:alnum:]-]\+\):\s\(.\{-\}\)'. "\r\\=$")
if len(header)
let k = tolower(header[1])
if has_key(a:result.headers, k)
let a:result.headers[k] .= ', ' . header[2]
else
let a:result.headers[k] = header[2]
endif
elseif empty(line)
break
2021-05-05 04:25:00 -04:00
endif
2021-10-11 05:30:43 -04:00
endfor
endfunction
let s:remote_headers = {}
function! fugitive#RemoteHttpHeaders(remote) abort
let remote = type(a:remote) ==# type({}) ? get(a:remote, 'remote', '') : a:remote
if type(remote) !=# type('') || remote !~# '^https\=://.' || !s:executable('curl')
return {}
2021-07-30 16:52:54 -04:00
endif
2022-08-08 09:45:56 -04:00
let remote = substitute(remote, '#.*', '', '')
2021-10-11 05:30:43 -04:00
if !has_key(s:remote_headers, remote)
let url = remote . '/info/refs?service=git-upload-pack'
let exec = s:JobExecute(
\ ['curl', '--disable', '--silent', '--max-time', '5', '-X', 'GET', '-I',
\ url], {}, [], [function('s:CurlResponse')], {})
call fugitive#Wait(exec)
let s:remote_headers[remote] = exec.headers
endif
return s:remote_headers[remote]
endfunction
function! s:UrlParse(url) abort
let scp_authority = matchstr(a:url, '^[^:/]\+\ze:\%(//\)\@!')
if len(scp_authority) && !(has('win32') && scp_authority =~# '^\a:[\/]')
2022-08-08 09:45:56 -04:00
let url = {'scheme': 'ssh', 'authority': s:UrlEncode(scp_authority), 'hash': '',
\ 'path': s:UrlEncode(strpart(a:url, len(scp_authority) + 1))}
2021-10-28 15:48:21 -04:00
elseif empty(a:url)
2022-08-08 09:45:56 -04:00
let url = {'scheme': '', 'authority': '', 'path': '', 'hash': ''}
2021-10-28 15:48:21 -04:00
else
2022-08-08 09:45:56 -04:00
let match = matchlist(a:url, '^\([[:alnum:].+-]\+\)://\([^/]*\)\(/[^#]*\)\=\(#.*\)\=$')
2021-10-28 15:48:21 -04:00
if empty(match)
2022-08-08 09:45:56 -04:00
let url = {'scheme': 'file', 'authority': '', 'hash': '',
\ 'path': s:UrlEncode(a:url)}
2021-10-28 15:48:21 -04:00
else
2022-08-08 09:45:56 -04:00
let url = {'scheme': match[1], 'authority': match[2], 'hash': match[4]}
2021-10-28 15:48:21 -04:00
let url.path = empty(match[3]) ? '/' : match[3]
endif
2021-10-11 05:30:43 -04:00
endif
2022-08-08 09:45:56 -04:00
return url
endfunction
function! s:UrlPopulate(string, into) abort
let url = a:into
let url.protocol = substitute(url.scheme, '.\zs$', ':', '')
let url.user = fugitive#UrlDecode(matchstr(url.authority, '.\{-\}\ze@', '', ''))
let url.host = substitute(url.authority, '.\{-\}@', '', '')
let url.hostname = substitute(url.host, ':\d\+$', '', '')
let url.port = matchstr(url.host, ':\zs\d\+$', '', '')
let url.origin = substitute(url.scheme, '.\zs$', '://', '') . url.host
let url.search = matchstr(url.path, '?.*')
let url.pathname = '/' . matchstr(url.path, '^/\=\zs[^?]*')
2021-10-28 15:48:21 -04:00
if (url.scheme ==# 'ssh' || url.scheme ==# 'git') && url.path[0:1] ==# '/~'
let url.path = strpart(url.path, 1)
2021-10-11 05:30:43 -04:00
endif
2022-08-08 09:45:56 -04:00
if url.path =~# '^/'
let url.href = url.scheme . '://' . url.authority . url.path . url.hash
elseif url.path =~# '^\~'
let url.href = url.scheme . '://' . url.authority . '/' . url.path . url.hash
elseif url.scheme ==# 'ssh' && url.authority !~# ':'
let url.href = url.authority . ':' . url.path . url.hash
else
let url.href = a:string
endif
let url.path = fugitive#UrlDecode(matchstr(url.path, '^[^?]*'))
let url.url = matchstr(url.href, '^[^#]*')
2021-10-11 05:30:43 -04:00
endfunction
2021-10-28 15:48:21 -04:00
function! s:RemoteResolve(url, flags) abort
2021-10-11 05:30:43 -04:00
let remote = s:UrlParse(a:url)
2021-10-28 15:48:21 -04:00
if remote.scheme =~# '^https\=$' && index(a:flags, ':nohttp') < 0
2022-08-08 09:45:56 -04:00
let headers = fugitive#RemoteHttpHeaders(a:url)
2021-10-11 05:30:43 -04:00
let loc = matchstr(get(headers, 'location', ''), '^https\=://.\{-\}\ze/info/refs?')
if len(loc)
let remote = s:UrlParse(loc)
else
2021-10-28 15:48:21 -04:00
let remote.headers = headers
2021-10-11 05:30:43 -04:00
endif
elseif remote.scheme ==# 'ssh'
let remote.authority = fugitive#SshHostAlias(remote.authority)
2021-07-30 16:52:54 -04:00
endif
2021-10-11 05:30:43 -04:00
return remote
endfunction
2021-10-28 15:48:21 -04:00
function! s:ConfigLengthSort(i1, i2) abort
return len(a:i2[0]) - len(a:i1[0])
endfunction
function! s:RemoteCallback(config, into, flags, cb) abort
if a:into.remote_name =~# '^\.\=$'
let a:into.remote_name = s:RemoteDefault(a:config)
endif
let url = a:into.remote_name
if url ==# '.git'
let url = s:GitDir(a:config)
elseif url !~# ':\|^/\|^\a:[\/]\|^\.\.\=/'
let url = FugitiveConfigGet('remote.' . url . '.url', a:config)
endif
let instead_of = []
for [k, vs] in items(fugitive#ConfigGetRegexp('^url\.\zs.\{-\}\ze\.insteadof$', a:config))
for v in vs
call add(instead_of, [v, k])
endfor
endfor
call sort(instead_of, 's:ConfigLengthSort')
for [orig, replacement] in instead_of
if strpart(url, 0, len(orig)) ==# orig
let url = replacement . strpart(url, len(orig))
break
endif
endfor
if index(a:flags, ':noresolve') < 0
call extend(a:into, s:RemoteResolve(url, a:flags))
2021-07-30 16:52:54 -04:00
else
2021-10-28 15:48:21 -04:00
call extend(a:into, s:UrlParse(url))
endif
2022-08-08 09:45:56 -04:00
call s:UrlPopulate(url, a:into)
2021-10-28 15:48:21 -04:00
if len(a:cb)
call call(a:cb[0], [a:into] + a:cb[1:-1])
2021-05-05 04:25:00 -04:00
endif
2021-07-30 16:52:54 -04:00
endfunction
2021-10-28 15:48:21 -04:00
function! s:Remote(dir, remote, flags, cb) abort
let into = {'remote_name': a:remote, 'git_dir': s:GitDir(a:dir)}
let config = fugitive#Config(a:dir, function('s:RemoteCallback'), into, a:flags, a:cb)
if len(a:cb)
return config
else
call fugitive#Wait(config)
return into
endif
2021-05-05 04:25:00 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:RemoteParseArgs(args) abort
" Extract ':noresolve' style flags and an optional callback
let args = []
let flags = []
let cb = copy(a:args)
while len(cb)
if type(cb[0]) ==# type(function('tr'))
break
elseif len(args) > 1 || type(cb[0]) ==# type('') && cb[0] =~# '^:'
call add(flags, remove(cb, 0))
else
call add(args, remove(cb, 0))
endif
endwhile
" From the remaining 0-2 arguments, extract the remote and Git config
let remote = ''
if empty(args)
let dir_or_config = s:Dir()
elseif len(args) == 1 && type(args[0]) ==# type('') && args[0] !~# '^/\|^\a:[\\/]'
let dir_or_config = s:Dir()
let remote = args[0]
elseif len(args) == 1
let dir_or_config = args[0]
if type(args[0]) ==# type({}) && has_key(args[0], 'remote_name')
let remote = args[0].remote_name
endif
elseif type(args[1]) !=# type('') || args[1] =~# '^/\|^\a:[\\/]'
let dir_or_config = args[1]
let remote = args[0]
else
let dir_or_config = args[0]
let remote = args[1]
endif
return [dir_or_config, remote, flags, cb]
endfunction
2021-10-28 15:48:21 -04:00
function! fugitive#Remote(...) abort
let [dir_or_config, remote, flags, cb] = s:RemoteParseArgs(a:000)
return s:Remote(dir_or_config, remote, flags, cb)
endfunction
function! s:RemoteUrlCallback(remote, callback) abort
return call(a:callback[0], [a:remote.url] + a:callback[1:-1])
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#RemoteUrl(...) abort
2022-08-08 09:45:56 -04:00
let [dir_or_config, remote_url, flags, cb] = s:RemoteParseArgs(a:000)
2021-10-28 15:48:21 -04:00
if len(cb)
let cb = [function('s:RemoteUrlCallback'), cb]
2021-05-05 04:25:00 -04:00
endif
2022-08-08 09:45:56 -04:00
let remote = s:Remote(dir_or_config, remote_url, flags, cb)
return get(remote, 'url', remote_url)
2018-07-30 17:18:16 -04:00
endfunction
2020-04-25 21:56:16 -04:00
" Section: Quickfix
function! s:QuickfixGet(nr, ...) abort
if a:nr < 0
return call('getqflist', a:000)
else
return call('getloclist', [a:nr] + a:000)
endif
endfunction
function! s:QuickfixSet(nr, ...) abort
if a:nr < 0
return call('setqflist', a:000)
else
return call('setloclist', [a:nr] + a:000)
endif
endfunction
function! s:QuickfixCreate(nr, opts) abort
if has('patch-7.4.2200')
call s:QuickfixSet(a:nr, [], ' ', a:opts)
else
call s:QuickfixSet(a:nr, [], ' ')
endif
endfunction
2021-10-11 05:30:43 -04:00
function! s:QuickfixOpen(nr, mods) abort
let mods = substitute(s:Mods(a:mods), '\<tab\>', '', '')
return mods . (a:nr < 0 ? 'c' : 'l').'open' . (mods =~# '\<vertical\>' ? ' 20' : '')
endfunction
2021-05-05 04:25:00 -04:00
function! s:QuickfixStream(nr, event, title, cmd, first, mods, callback, ...) abort
2021-07-30 16:52:54 -04:00
call s:BlurStatus()
2020-04-25 21:56:16 -04:00
let opts = {'title': a:title, 'context': {'items': []}}
call s:QuickfixCreate(a:nr, opts)
let event = (a:nr < 0 ? 'c' : 'l') . 'fugitive-' . a:event
2022-08-08 09:45:56 -04:00
exe s:DoAutocmd('QuickFixCmdPre ' . event)
2020-04-25 21:56:16 -04:00
let winnr = winnr()
2021-10-11 05:30:43 -04:00
exe s:QuickfixOpen(a:nr, a:mods)
2020-04-25 21:56:16 -04:00
if winnr != winnr()
wincmd p
endif
let buffer = []
2021-10-11 05:30:43 -04:00
let lines = s:SystemList(a:cmd)[0]
2020-04-25 21:56:16 -04:00
for line in lines
call extend(buffer, call(a:callback, a:000 + [line]))
if len(buffer) >= 20
let contexts = map(copy(buffer), 'get(v:val, "context", {})')
lockvar contexts
call extend(opts.context.items, contexts)
unlet contexts
call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
2021-10-11 05:30:43 -04:00
if a:mods !~# '\<silent\>'
2021-05-05 04:25:00 -04:00
redraw
endif
2020-04-25 21:56:16 -04:00
endif
endfor
call extend(buffer, call(a:callback, a:000 + [0]))
call extend(opts.context.items, map(copy(buffer), 'get(v:val, "context", {})'))
lockvar opts.context.items
call s:QuickfixSet(a:nr, buffer, 'a')
2022-08-08 09:45:56 -04:00
exe s:DoAutocmd('QuickFixCmdPost ' . event)
2020-04-25 21:56:16 -04:00
if a:first && len(s:QuickfixGet(a:nr))
2021-10-11 05:30:43 -04:00
return (a:nr < 0 ? 'cfirst' : 'lfirst')
2020-04-25 21:56:16 -04:00
else
return 'exe'
endif
endfunction
function! fugitive#Cwindow() abort
if &buftype == 'quickfix'
cwindow
else
botright cwindow
if &buftype == 'quickfix'
wincmd p
endif
endif
endfunction
2018-08-25 12:13:42 -04:00
" Section: Repository Object
2018-06-14 06:31:12 -04:00
let s:repo_prototype = {}
2018-07-30 17:18:16 -04:00
function! fugitive#repo(...) abort
2021-10-11 05:30:43 -04:00
let dir = a:0 ? s:GitDir(a:1) : (len(s:GitDir()) ? s:GitDir() : FugitiveExtractGitDir(expand('%:p')))
2018-06-14 06:31:12 -04:00
if dir !=# ''
2022-08-08 09:45:56 -04:00
return extend({'git_dir': dir, 'fugitive_dir': dir}, s:repo_prototype, 'keep')
2018-06-14 06:31:12 -04:00
endif
2022-08-08 09:45:56 -04:00
throw 'fugitive: not a Git repository'
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_dir(...) dict abort
if !a:0
return self.git_dir
endif
throw 'fugitive: fugitive#repo().dir("...") has been replaced by FugitiveFind(".git/...")'
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_tree(...) dict abort
let tree = s:Tree(self.git_dir)
if empty(tree)
throw 'fugitive: no work tree'
elseif !a:0
return tree
endif
throw 'fugitive: fugitive#repo().tree("...") has been replaced by FugitiveFind(":(top)...")'
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_bare() dict abort
2022-08-08 09:45:56 -04:00
throw 'fugitive: fugitive#repo().bare() has been replaced by !empty(FugitiveWorkTree())'
2018-07-30 17:18:16 -04:00
endfunction
2018-11-01 06:03:42 -04:00
function! s:repo_find(object) dict abort
2022-08-08 09:45:56 -04:00
throw 'fugitive: fugitive#repo().find(...) has been replaced by FugitiveFind(...)'
2018-07-30 17:18:16 -04:00
endfunction
2018-08-25 12:13:42 -04:00
function! s:repo_translate(rev) dict abort
2022-08-08 09:45:56 -04:00
throw 'fugitive: fugitive#repo().translate(...) has been replaced by FugitiveFind(...)'
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_head(...) dict abort
2022-08-08 09:45:56 -04:00
throw 'fugitive: fugitive#repo().head(...) has been replaced by FugitiveHead(...)'
2018-06-14 06:31:12 -04:00
endfunction
2018-11-01 06:03:42 -04:00
call s:add_methods('repo',['dir','tree','bare','find','translate','head'])
2018-06-14 06:31:12 -04:00
function! s:repo_git_command(...) dict abort
2021-10-11 05:30:43 -04:00
throw 'fugitive: fugitive#repo().git_command(...) has been replaced by FugitiveShellCommand(...)'
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_git_chomp(...) dict abort
silent return substitute(system(fugitive#ShellCommand(a:000, self.git_dir)), '\n$', '', '')
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_git_chomp_in_tree(...) dict abort
return call(self.git_chomp, a:000, self)
2018-06-14 06:31:12 -04:00
endfunction
function! s:repo_rev_parse(rev) dict abort
2022-08-08 09:45:56 -04:00
throw 'fugitive: fugitive#repo().rev_parse(...) has been replaced by FugitiveExecute("rev-parse", "--verify", ...).stdout'
2018-06-14 06:31:12 -04:00
endfunction
2021-10-11 05:30:43 -04:00
call s:add_methods('repo',['git_command','git_chomp','git_chomp_in_tree','rev_parse'])
2018-06-14 06:31:12 -04:00
2018-07-30 17:18:16 -04:00
function! s:repo_config(name) dict abort
return FugitiveConfigGet(a:name, self.git_dir)
2018-06-14 06:31:12 -04:00
endfunction
2022-08-08 09:45:56 -04:00
call s:add_methods('repo',['config'])
2018-06-14 06:31:12 -04:00
2018-08-25 12:13:42 -04:00
" Section: File API
2018-06-14 06:31:12 -04:00
2018-07-04 06:53:25 -04:00
function! s:DirCommitFile(path) abort
2022-08-08 09:45:56 -04:00
let vals = matchlist(s:Slash(a:path), s:dir_commit_file)
2018-07-04 06:53:25 -04:00
if empty(vals)
return ['', '', '']
endif
2022-08-08 09:45:56 -04:00
return [s:Dir(fugitive#UrlDecode(vals[1])), vals[2], empty(vals[2]) ? '/.git/index' : fugitive#UrlDecode(vals[3])]
2018-07-19 08:52:53 -04:00
endfunction
function! s:DirRev(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
2022-08-08 09:45:56 -04:00
return [dir, commit . file ==# '/.git/index' ? ':' : (!empty(dir) && commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
endfunction
function! fugitive#Parse(url) abort
return reverse(s:DirRev(a:url))
2018-07-04 06:53:25 -04:00
endfunction
2019-11-16 10:28:42 -05:00
let s:merge_heads = ['MERGE_HEAD', 'REBASE_HEAD', 'CHERRY_PICK_HEAD', 'REVERT_HEAD']
2021-10-11 05:30:43 -04:00
function! s:MergeHead(dir) abort
let dir = fugitive#Find('.git/', a:dir)
2019-11-16 10:28:42 -05:00
for head in s:merge_heads
if filereadable(dir . head)
return head
endif
endfor
return ''
endfunction
2018-08-25 12:13:42 -04:00
function! s:Owner(path, ...) abort
2021-10-11 05:30:43 -04:00
let dir = a:0 ? s:Dir(a:1) : s:Dir()
2018-08-25 12:13:42 -04:00
if empty(dir)
return ''
endif
2019-08-22 11:36:17 -04:00
let actualdir = fugitive#Find('.git/', dir)
2018-08-25 12:13:42 -04:00
let [pdir, commit, file] = s:DirCommitFile(a:path)
2022-08-08 09:45:56 -04:00
if s:SameRepo(dir, pdir)
2019-08-22 11:36:17 -04:00
if commit =~# '^\x\{40,\}$'
2018-11-01 06:03:42 -04:00
return commit
elseif commit ==# '2'
2021-05-05 04:25:00 -04:00
return '@'
2019-11-16 10:28:42 -05:00
elseif commit ==# '0'
return ''
2018-11-01 06:03:42 -04:00
endif
2021-05-05 04:25:00 -04:00
let merge_head = s:MergeHead(dir)
2019-11-16 10:28:42 -05:00
if empty(merge_head)
2018-11-01 06:03:42 -04:00
return ''
endif
if commit ==# '3'
2021-05-05 04:25:00 -04:00
return merge_head
2018-11-01 06:03:42 -04:00
elseif commit ==# '1'
return s:TreeChomp('merge-base', 'HEAD', merge_head, '--')
endif
2018-08-25 12:13:42 -04:00
endif
let path = fnamemodify(a:path, ':p')
2019-08-22 11:36:17 -04:00
if s:cpath(actualdir, strpart(path, 0, len(actualdir))) && a:path =~# 'HEAD$'
return strpart(path, len(actualdir))
2018-08-25 12:13:42 -04:00
endif
2019-08-22 11:36:17 -04:00
let refs = fugitive#Find('.git/refs', dir)
2018-08-25 12:13:42 -04:00
if s:cpath(refs . '/', path[0 : len(refs)]) && path !~# '[\/]$'
return strpart(path, len(refs) - 4)
endif
return ''
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#Real(url) abort
2018-08-25 12:13:42 -04:00
if empty(a:url)
return ''
endif
2018-07-04 06:53:25 -04:00
let [dir, commit, file] = s:DirCommitFile(a:url)
if len(dir)
2018-07-30 17:18:16 -04:00
let tree = s:Tree(dir)
2022-08-08 09:45:56 -04:00
return s:VimSlash((len(tree) ? tree : s:GitDir(dir)) . file)
2018-07-30 17:18:16 -04:00
endif
2018-08-25 12:13:42 -04:00
let pre = substitute(matchstr(a:url, '^\a\a\+\ze:'), '^.', '\u&', '')
if len(pre) && pre !=? 'fugitive' && exists('*' . pre . 'Real')
let url = {pre}Real(a:url)
else
let url = fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??'))
2018-07-04 06:53:25 -04:00
endif
2022-08-08 09:45:56 -04:00
return s:VimSlash(empty(url) ? a:url : url)
2018-07-04 06:53:25 -04:00
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#Path(url, ...) abort
2018-09-24 20:40:17 -04:00
if empty(a:url)
return ''
endif
2022-08-08 09:45:56 -04:00
let repo = call('s:Dir', a:000[1:-1])
let dir_s = fugitive#Find('.git/', repo)
let tree = fugitive#Find(':/', repo)
2018-09-24 20:40:17 -04:00
if !a:0
2018-07-30 17:18:16 -04:00
return fugitive#Real(a:url)
2018-09-24 20:40:17 -04:00
elseif a:1 =~# '\.$'
let path = s:Slash(fugitive#Real(a:url))
let cwd = getcwd()
let lead = ''
while s:cpath(tree . '/', (cwd . '/')[0 : len(tree)])
if s:cpath(cwd . '/', path[0 : len(cwd)])
if strpart(path, len(cwd) + 1) =~# '^\.git\%(/\|$\)'
break
endif
return a:1[0:-2] . (empty(lead) ? './' : lead) . strpart(path, len(cwd) + 1)
endif
let cwd = fnamemodify(cwd, ':h')
let lead .= '../'
endwhile
return a:1[0:-2] . path
2018-07-30 17:18:16 -04:00
endif
2019-03-08 06:04:56 -05:00
let url = a:url
2019-08-22 11:36:17 -04:00
let temp_state = s:TempState(url)
2021-05-05 04:25:00 -04:00
if has_key(temp_state, 'origin_bufnr')
let url = bufname(temp_state.origin_bufnr)
2019-03-08 06:04:56 -05:00
endif
let url = s:Slash(fnamemodify(url, ':p'))
2018-08-25 12:13:42 -04:00
if url =~# '/$' && s:Slash(a:url) !~# '/$'
2018-07-30 17:18:16 -04:00
let url = url[0:-2]
endif
2022-08-08 09:45:56 -04:00
let [argdir, commit, file] = s:DirCommitFile(url)
if !empty(argdir) && !s:SameRepo(argdir, repo)
2018-07-30 17:18:16 -04:00
let file = ''
2022-08-08 09:45:56 -04:00
elseif len(dir_s) && s:cpath(strpart(url, 0, len(dir_s)), dir_s)
let file = '/.git' . strpart(url, len(dir_s)-1)
2018-07-30 17:18:16 -04:00
elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/')
let file = url[len(tree) : -1]
2019-01-08 05:11:54 -05:00
elseif s:cpath(url) ==# s:cpath(tree)
2018-07-30 17:18:16 -04:00
let file = '/'
endif
2018-08-25 12:13:42 -04:00
if empty(file) && a:1 =~# '^$\|^[.:]/$'
2019-08-22 11:36:17 -04:00
return FugitiveGitPath(fugitive#Real(a:url))
2018-07-30 17:18:16 -04:00
endif
2022-08-08 09:45:56 -04:00
return substitute(file, '^/', '\=a:1', '')
2018-07-04 06:53:25 -04:00
endfunction
2018-08-25 12:13:42 -04:00
function! s:Relative(...) abort
2019-08-22 11:36:17 -04:00
return fugitive#Path(@%, a:0 ? a:1 : ':(top)', a:0 > 1 ? a:2 : s:Dir())
2018-08-25 12:13:42 -04:00
endfunction
2018-11-01 06:03:42 -04:00
function! fugitive#Find(object, ...) abort
2018-09-24 20:40:17 -04:00
if type(a:object) == type(0)
let name = bufname(a:object)
2022-08-08 09:45:56 -04:00
return s:VimSlash(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name)
2018-09-24 20:40:17 -04:00
elseif a:object =~# '^[~$]'
2018-08-25 12:13:42 -04:00
let prefix = matchstr(a:object, '^[~$]\i*')
let owner = expand(prefix)
2022-08-08 09:45:56 -04:00
return s:VimSlash(FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix))))
2021-05-05 04:25:00 -04:00
endif
let rev = s:Slash(a:object)
2022-08-08 09:45:56 -04:00
if rev =~# '^\a\+://' && rev !~# '^fugitive:'
return rev
elseif rev =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
return s:VimSlash(a:object)
2021-05-05 04:25:00 -04:00
elseif rev =~# '^\.\.\=\%(/\|$\)'
2022-08-08 09:45:56 -04:00
return s:VimSlash(simplify(getcwd() . '/' . a:object))
2018-08-25 12:13:42 -04:00
endif
2021-10-11 05:30:43 -04:00
let dir = call('s:GitDir', a:000)
2018-08-25 12:13:42 -04:00
if empty(dir)
2021-05-05 04:25:00 -04:00
let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs\%(\.\.\=$\|\.\.\=/.*\|/.*\|\w:/.*\)')
2018-08-25 12:13:42 -04:00
let dir = FugitiveExtractGitDir(file)
if empty(dir)
2021-05-05 04:25:00 -04:00
return ''
2018-08-25 12:13:42 -04:00
endif
endif
let tree = s:Tree(dir)
2021-10-11 05:30:43 -04:00
let urlprefix = s:DirUrlPrefix(dir)
let base = len(tree) ? tree : urlprefix . '0'
2018-08-25 12:13:42 -04:00
if rev ==# '.git'
2021-10-11 05:30:43 -04:00
let f = len(tree) && len(getftype(tree . '/.git')) ? tree . '/.git' : dir
2018-08-25 12:13:42 -04:00
elseif rev =~# '^\.git/'
2021-07-30 16:52:54 -04:00
let f = strpart(rev, 5)
2022-08-08 09:45:56 -04:00
let fdir = simplify(FugitiveActualDir(dir) . '/')
let cdir = simplify(FugitiveCommonDir(dir) . '/')
2021-07-30 16:52:54 -04:00
if f =~# '^\.\./\.\.\%(/\|$\)'
let f = simplify(len(tree) ? tree . f[2:-1] : fdir . f)
elseif f =~# '^\.\.\%(/\|$\)'
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(base, f[2:-1])
2021-07-30 16:52:54 -04:00
elseif cdir !=# fdir && (
\ f =~# '^\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' ||
\ f !~# '^\%(index$\|index\.lock$\|\w*MSG$\|\w*HEAD$\|logs/\w*HEAD$\|logs$\|rebase-\w\+\)\%(/\|$\)' &&
2022-08-08 09:45:56 -04:00
\ getftime(fdir . f) < 0 && getftime(cdir . f) >= 0)
2018-08-25 12:13:42 -04:00
let f = simplify(cdir . f)
else
2021-07-30 16:52:54 -04:00
let f = simplify(fdir . f)
2018-08-25 12:13:42 -04:00
endif
elseif rev ==# ':/'
2021-05-05 04:25:00 -04:00
let f = tree
2018-08-25 12:13:42 -04:00
elseif rev =~# '^\.\%(/\|$\)'
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(base, rev[1:-1])
2018-08-25 12:13:42 -04:00
elseif rev =~# '^::\%(/\|\a\+\:\)'
let f = rev[2:-1]
elseif rev =~# '^::\.\.\=\%(/\|$\)'
let f = simplify(getcwd() . '/' . rev[2:-1])
elseif rev =~# '^::'
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(base, '/' . rev[2:-1])
2018-08-25 12:13:42 -04:00
elseif rev =~# '^:\%([0-3]:\)\=\.\.\=\%(/\|$\)\|^:[0-3]:\%(/\|\a\+:\)'
let f = rev =~# '^:\%([0-3]:\)\=\.' ? simplify(getcwd() . '/' . matchstr(rev, '\..*')) : rev[3:-1]
if s:cpath(base . '/', (f . '/')[0 : len(base)])
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(urlprefix, +matchstr(rev, '^:\zs\d\ze:') . '/' . strpart(f, len(base) + 1))
2018-08-25 12:13:42 -04:00
else
let altdir = FugitiveExtractGitDir(f)
if len(altdir) && !s:cpath(dir, altdir)
2018-11-01 06:03:42 -04:00
return fugitive#Find(a:object, altdir)
2018-08-25 12:13:42 -04:00
endif
endif
elseif rev =~# '^:[0-3]:'
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(urlprefix, rev[1] . '/' . rev[3:-1])
2018-08-25 12:13:42 -04:00
elseif rev ==# ':'
2022-08-08 09:45:56 -04:00
let f = urlprefix
2018-08-25 12:13:42 -04:00
elseif rev =~# '^:(\%(top\|top,literal\|literal,top\|literal\))'
2019-08-22 11:36:17 -04:00
let f = matchstr(rev, ')\zs.*')
if f=~# '^\.\.\=\%(/\|$\)'
let f = simplify(getcwd() . '/' . f)
elseif f !~# '^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(base, '/' . f)
2019-08-22 11:36:17 -04:00
endif
2018-08-25 12:13:42 -04:00
elseif rev =~# '^:/\@!'
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(urlprefix, '0/' . rev[1:-1])
2018-08-25 12:13:42 -04:00
else
if !exists('f')
2021-05-05 04:25:00 -04:00
let commit = matchstr(rev, '^\%([^:.-]\|\.\.[^/:]\)[^:]*\|^:.*')
2020-01-07 07:45:07 -05:00
let file = substitute(matchstr(rev, '^\%([^:.-]\|\.\.[^/:]\)[^:]*\zs:.*'), '^:', '/', '')
2018-08-25 12:13:42 -04:00
if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:'
let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1]
if s:cpath(base . '/', (file . '/')[0 : len(base)])
let file = '/' . strpart(file, len(base) + 1)
else
let altdir = FugitiveExtractGitDir(file)
if len(altdir) && !s:cpath(dir, altdir)
2018-11-01 06:03:42 -04:00
return fugitive#Find(a:object, altdir)
2018-08-25 12:13:42 -04:00
endif
return file
endif
endif
2019-08-22 11:36:17 -04:00
let commits = split(commit, '\.\.\.-\@!', 1)
if len(commits) == 2
2021-05-05 04:25:00 -04:00
call map(commits, 'empty(v:val) ? "@" : v:val')
2019-08-22 11:36:17 -04:00
let commit = matchstr(s:ChompDefault('', [dir, 'merge-base'] + commits + ['--']), '\<[0-9a-f]\{40,\}\>')
endif
2021-05-05 04:25:00 -04:00
if commit !~# '^[0-9a-f]\{40,\}$\|^$'
2019-12-30 08:28:38 -05:00
let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit . (len(file) ? '^{}' : ''), '--']), '\<[0-9a-f]\{40,\}\>')
2021-05-05 04:25:00 -04:00
if empty(commit) && len(file)
let commit = repeat('0', 40)
endif
2018-08-25 12:13:42 -04:00
endif
if len(commit)
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(urlprefix, commit . file)
2018-08-25 12:13:42 -04:00
else
2022-08-08 09:45:56 -04:00
let f = s:PathJoin(base, '/' . substitute(rev, '^:/:\=\|^[^:]\+:', '', ''))
2018-08-25 12:13:42 -04:00
endif
endif
endif
2022-08-08 09:45:56 -04:00
return s:VimSlash(f)
2018-08-25 12:13:42 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! s:Generate(object, ...) abort
let dir = a:0 ? a:1 : s:Dir()
let f = fugitive#Find(a:object, dir)
if !empty(f)
return f
elseif a:object ==# ':/'
2022-08-08 09:45:56 -04:00
return len(dir) ? s:VimSlash(s:DirUrlPrefix(dir) . '0') : '.'
2021-05-05 04:25:00 -04:00
endif
2022-08-08 09:45:56 -04:00
let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\=\zs.*')
return empty(file) ? '' : fnamemodify(s:VimSlash(file), ':p')
2018-08-25 12:13:42 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:DotRelative(path, ...) abort
let cwd = a:0 ? a:1 : getcwd()
2018-09-24 20:40:17 -04:00
let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
2019-08-22 11:36:17 -04:00
if len(cwd) && s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
2018-09-24 20:40:17 -04:00
return '.' . strpart(path, len(cwd))
2018-08-25 12:13:42 -04:00
endif
2018-09-24 20:40:17 -04:00
return a:path
2018-08-25 12:13:42 -04:00
endfunction
function! fugitive#Object(...) abort
2021-10-11 05:30:43 -04:00
let dir = a:0 > 1 ? s:Dir(a:2) : s:Dir()
2018-08-25 12:13:42 -04:00
let [fdir, rev] = s:DirRev(a:0 ? a:1 : @%)
2022-08-08 09:45:56 -04:00
if !s:SameRepo(dir, fdir)
2018-08-25 12:13:42 -04:00
let rev = ''
endif
let tree = s:Tree(dir)
2021-05-05 04:25:00 -04:00
let full = a:0 ? a:1 : s:BufName('%')
2019-11-16 10:28:42 -05:00
let full = fnamemodify(full, ':p' . (s:Slash(full) =~# '/$' ? '' : ':s?/$??'))
2018-08-25 12:13:42 -04:00
if empty(rev) && empty(tree)
2019-11-16 10:28:42 -05:00
return FugitiveGitPath(full)
2018-08-25 12:13:42 -04:00
elseif empty(rev)
2019-11-16 10:28:42 -05:00
let rev = fugitive#Path(full, './', dir)
2019-08-22 11:36:17 -04:00
if rev =~# '^\./.git\%(/\|$\)'
2019-11-16 10:28:42 -05:00
return FugitiveGitPath(full)
2018-08-25 12:13:42 -04:00
endif
endif
if rev !~# '^\.\%(/\|$\)' || s:cpath(getcwd(), tree)
return rev
else
2019-11-16 10:28:42 -05:00
return FugitiveGitPath(tree . rev[1:-1])
2018-08-25 12:13:42 -04:00
endif
endfunction
2021-05-05 04:25:00 -04:00
let s:var = '\%(<\%(cword\|cWORD\|cexpr\|cfile\|sfile\|slnum\|afile\|abuf\|amatch' . (has('clientserver') ? '\|client' : '') . '\)>\|%\|#<\=\d\+\|##\=\)'
2018-09-24 20:40:17 -04:00
let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
2022-11-20 06:21:23 -05:00
let s:commit_expand = '!\\\@!#\=\d*\|!%'
2018-09-24 20:40:17 -04:00
function! s:BufName(var) abort
if a:var ==# '%'
2021-05-05 04:25:00 -04:00
return bufname(get(s:TempState(), 'origin_bufnr', ''))
2018-09-24 20:40:17 -04:00
elseif a:var =~# '^#\d*$'
2022-08-08 09:45:56 -04:00
let nr = get(s:TempState(+a:var[1:-1]), 'origin_bufnr', '')
2018-09-24 20:40:17 -04:00
return bufname(nr ? nr : +a:var[1:-1])
else
return expand(a:var)
endif
endfunction
2019-08-22 11:36:17 -04:00
function! s:ExpandVar(other, var, flags, esc, ...) abort
let cwd = a:0 ? a:1 : getcwd()
2018-09-24 20:40:17 -04:00
if a:other =~# '^\'
return a:other[1:-1]
2019-08-22 11:36:17 -04:00
elseif a:other =~# '^'''
2022-08-08 09:45:56 -04:00
return substitute(a:other[1:-2], "''", "'", "g")
2019-08-22 11:36:17 -04:00
elseif a:other =~# '^"'
2022-08-08 09:45:56 -04:00
return substitute(a:other[1:-2], '""', '"', "g")
2022-11-20 06:21:23 -05:00
elseif a:other =~# '^[!`]'
let buffer = s:BufName(a:other =~# '[0-9#]' ? '#' . matchstr(a:other, '\d\+') : '%')
2018-09-24 20:40:17 -04:00
let owner = s:Owner(buffer)
return len(owner) ? owner : '@'
2021-10-11 05:30:43 -04:00
elseif a:other =~# '^\~[~.]$'
return s:Slash(getcwd())
elseif len(a:other)
return expand(a:other)
2021-05-05 04:25:00 -04:00
elseif a:var ==# '<cfile>'
2021-07-30 16:52:54 -04:00
let bufnames = [expand('<cfile>')]
2022-08-08 09:45:56 -04:00
if get(maparg('<Plug><cfile>', 'c', 0, 1), 'expr')
2021-05-05 04:25:00 -04:00
try
2021-07-30 16:52:54 -04:00
let bufnames = [eval(maparg('<Plug><cfile>', 'c'))]
if bufnames[0] ==# "\<C-R>\<C-F>"
let bufnames = [expand('<cfile>')]
2021-05-05 04:25:00 -04:00
endif
catch
endtry
endif
elseif a:var =~# '^<'
2021-07-30 16:52:54 -04:00
let bufnames = [s:BufName(a:var)]
elseif a:var ==# '##'
let bufnames = map(argv(), 'fugitive#Real(v:val)')
2021-05-05 04:25:00 -04:00
else
2021-07-30 16:52:54 -04:00
let bufnames = [fugitive#Real(s:BufName(a:var))]
2021-05-05 04:25:00 -04:00
endif
2021-07-30 16:52:54 -04:00
let files = []
for bufname in bufnames
let flags = a:flags
let file = s:DotRelative(bufname, cwd)
while len(flags)
let flag = matchstr(flags, s:flag)
let flags = strpart(flags, len(flag))
if flag ==# ':.'
let file = s:DotRelative(fugitive#Real(file), cwd)
else
let file = fnamemodify(file, flag)
endif
endwhile
let file = s:Slash(file)
if file =~# '^fugitive://'
let [dir, commit, file_candidate] = s:DirCommitFile(file)
let tree = s:Tree(dir)
if len(tree) && len(file_candidate)
let file = (commit =~# '^.$' ? ':' : '') . commit . ':' .
\ s:DotRelative(tree . file_candidate)
elseif empty(file_candidate) && commit !~# '^.$'
let file = commit
endif
endif
call add(files, len(a:esc) ? shellescape(file) : file)
endfor
return join(files, "\1")
2018-09-24 20:40:17 -04:00
endfunction
2022-11-20 06:21:23 -05:00
if has('win32')
let s:fnameescape = " \t\n*?`%#'\"|!<"
else
let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
endif
2022-08-08 09:45:56 -04:00
2019-08-22 11:36:17 -04:00
function! s:Expand(rev, ...) abort
2022-08-08 09:45:56 -04:00
if a:rev =~# '^>' && s:Slash(@%) =~# '^fugitive://' && empty(s:DirCommitFile(@%)[1])
return s:Slash(@%)
elseif a:rev =~# '^>\=:[0-3]$'
2021-10-11 05:30:43 -04:00
let file = len(expand('%')) ? a:rev[-2:-1] . ':%' : '%'
elseif a:rev =~# '^>\%(:\=/\)\=$'
2019-11-16 10:28:42 -05:00
let file = '%'
2022-08-08 09:45:56 -04:00
elseif a:rev =~# '^>[> ]\@!' && @% !~# '^fugitive:' && s:Slash(@%) =~# '://\|^$'
let file = '%'
2021-10-11 05:30:43 -04:00
elseif a:rev ==# '>:'
let file = empty(s:DirCommitFile(@%)[0]) ? ':0:%' : '%'
2019-08-22 11:36:17 -04:00
elseif a:rev =~# '^>[> ]\@!'
2021-10-11 05:30:43 -04:00
let rev = (a:rev =~# '^>[~^]' ? '!' : '') . a:rev[1:-1]
let prefix = matchstr(rev, '^\%(\\.\|{[^{}]*}\|[^:]\)*')
if prefix !=# rev
let file = rev
else
let file = len(expand('%')) ? rev . ':%' : '%'
endif
2022-08-08 09:45:56 -04:00
elseif s:Slash(a:rev) =~# '^\a\a\+://'
let file = substitute(a:rev, '\\\@<!\%(#\a\|%\x\x\)', '\\&', 'g')
2018-08-25 12:13:42 -04:00
else
let file = a:rev
endif
2018-09-24 20:40:17 -04:00
return substitute(file,
2022-11-20 06:21:23 -05:00
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\|^\~[~.]\)\|' . s:expand,
2021-07-30 16:52:54 -04:00
\ '\=tr(s:ExpandVar(submatch(1),submatch(2),submatch(3),"", a:0 ? a:1 : getcwd()), "\1", " ")', 'g')
2018-09-24 20:40:17 -04:00
endfunction
function! fugitive#Expand(object) abort
return substitute(a:object,
2022-11-20 06:21:23 -05:00
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\|^\~[~.]\)\|' . s:expand,
2021-07-30 16:52:54 -04:00
\ '\=tr(s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5)), "\1", " ")', 'g')
2018-08-25 12:13:42 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! s:SplitExpandChain(string, ...) abort
2019-08-22 11:36:17 -04:00
let list = []
let string = a:string
2021-05-05 04:25:00 -04:00
let dquote = '"\%([^"]\|""\|\\"\)*"\|'
let cwd = a:0 ? a:1 : getcwd()
2019-08-22 11:36:17 -04:00
while string =~# '\S'
2021-05-05 04:25:00 -04:00
if string =~# '^\s*|'
2019-08-22 11:36:17 -04:00
return [list, substitute(string, '^\s*', '', '')]
endif
2022-08-08 09:45:56 -04:00
let arg = matchstr(string, '^\s*\%(' . dquote . '''[^'']*''\|\\.\|[^' . "\t" . ' |]\)\+')
2019-08-22 11:36:17 -04:00
let string = strpart(string, len(arg))
let arg = substitute(arg, '^\s\+', '', '')
if !exists('seen_separator')
2021-10-11 05:30:43 -04:00
let arg = substitute(arg, '^\%([^:.][^:]*:\|^:\%((literal)\)\=\|^:[0-3]:\)\=\zs\.\.\=\%(/.*\)\=$',
2019-08-22 11:36:17 -04:00
\ '\=s:DotRelative(s:Slash(simplify(getcwd() . "/" . submatch(0))), cwd)', '')
endif
let arg = substitute(arg,
2022-11-20 06:21:23 -05:00
\ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|' . s:commit_expand . '\|^\~[~]\|^\~\w*\|\$\w\+\)\|' . s:expand,
2019-08-22 11:36:17 -04:00
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5), cwd)', 'g')
2021-07-30 16:52:54 -04:00
call extend(list, split(arg, "\1", 1))
2019-08-22 11:36:17 -04:00
if arg ==# '--'
let seen_separator = 1
endif
endwhile
2021-05-05 04:25:00 -04:00
return [list, '']
2018-08-25 12:13:42 -04:00
endfunction
2018-07-04 06:53:25 -04:00
let s:trees = {}
let s:indexes = {}
function! s:TreeInfo(dir, commit) abort
2022-08-08 09:45:56 -04:00
let key = s:GitDir(a:dir)
2018-07-04 06:53:25 -04:00
if a:commit =~# '^:\=[0-3]$'
2022-08-08 09:45:56 -04:00
let index = get(s:indexes, key, [])
2019-08-22 11:36:17 -04:00
let newftime = getftime(fugitive#Find('.git/index', a:dir))
2022-08-08 09:45:56 -04:00
if get(index, 0, -2) < newftime
2019-08-22 11:36:17 -04:00
let [lines, exec_error] = s:LinesError([a:dir, 'ls-files', '--stage', '--'])
2022-08-08 09:45:56 -04:00
let s:indexes[key] = [newftime, {'0': {}, '1': {}, '2': {}, '3': {}}]
2019-08-22 11:36:17 -04:00
if exec_error
2018-07-04 06:53:25 -04:00
return [{}, -1]
endif
2019-08-22 11:36:17 -04:00
for line in lines
2018-07-04 06:53:25 -04:00
let [info, filename] = split(line, "\t")
let [mode, sha, stage] = split(info, '\s\+')
2022-08-08 09:45:56 -04:00
let s:indexes[key][1][stage][filename] = [newftime, mode, 'blob', sha, -2]
2018-07-04 06:53:25 -04:00
while filename =~# '/'
let filename = substitute(filename, '/[^/]*$', '', '')
2022-08-08 09:45:56 -04:00
let s:indexes[key][1][stage][filename] = [newftime, '040000', 'tree', '', 0]
2018-07-04 06:53:25 -04:00
endwhile
endfor
endif
2022-08-08 09:45:56 -04:00
return [get(s:indexes[key][1], a:commit[-1:-1], {}), newftime]
2019-08-22 11:36:17 -04:00
elseif a:commit =~# '^\x\{40,\}$'
2022-08-08 09:45:56 -04:00
if !has_key(s:trees, key)
let s:trees[key] = {}
2018-08-25 12:13:42 -04:00
endif
2022-08-08 09:45:56 -04:00
if !has_key(s:trees[key], a:commit)
2021-10-11 05:30:43 -04:00
let ftime = s:ChompDefault('', [a:dir, 'log', '-1', '--pretty=format:%ct', a:commit, '--'])
if empty(ftime)
2022-08-08 09:45:56 -04:00
let s:trees[key][a:commit] = [{}, -1]
return s:trees[key][a:commit]
2018-07-04 06:53:25 -04:00
endif
2022-08-08 09:45:56 -04:00
let s:trees[key][a:commit] = [{}, +ftime]
2019-08-22 11:36:17 -04:00
let [lines, exec_error] = s:LinesError([a:dir, 'ls-tree', '-rtl', '--full-name', a:commit, '--'])
if exec_error
2022-08-08 09:45:56 -04:00
return s:trees[key][a:commit]
2018-07-04 06:53:25 -04:00
endif
2019-08-22 11:36:17 -04:00
for line in lines
2018-07-04 06:53:25 -04:00
let [info, filename] = split(line, "\t")
let [mode, type, sha, size] = split(info, '\s\+')
2022-08-08 09:45:56 -04:00
let s:trees[key][a:commit][0][filename] = [+ftime, mode, type, sha, +size, filename]
2018-07-04 06:53:25 -04:00
endfor
endif
2022-08-08 09:45:56 -04:00
return s:trees[key][a:commit]
2018-07-04 06:53:25 -04:00
endif
return [{}, -1]
endfunction
function! s:PathInfo(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if empty(dir) || !get(g:, 'fugitive_file_api', 1)
return [-1, '000000', '', '', -1]
endif
let path = substitute(file[1:-1], '/*$', '', '')
let [tree, ftime] = s:TreeInfo(dir, commit)
let entry = empty(path) ? [ftime, '040000', 'tree', '', -1] : get(tree, path, [])
2019-08-22 11:36:17 -04:00
if empty(entry) || file =~# '/$' && entry[2] !=# 'tree'
2018-07-04 06:53:25 -04:00
return [-1, '000000', '', '', -1]
else
return entry
endif
endfunction
function! fugitive#simplify(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if empty(dir)
return ''
2022-08-08 09:45:56 -04:00
elseif empty(commit)
return s:VimSlash(s:DirUrlPrefix(simplify(s:GitDir(dir))))
2018-07-04 06:53:25 -04:00
endif
if file =~# '/\.\.\%(/\|$\)'
2018-07-30 17:18:16 -04:00
let tree = s:Tree(dir)
2018-07-04 06:53:25 -04:00
if len(tree)
let path = simplify(tree . file)
if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/'
2022-08-08 09:45:56 -04:00
return s:VimSlash(path)
2018-07-04 06:53:25 -04:00
endif
endif
endif
2022-08-08 09:45:56 -04:00
return s:VimSlash(s:PathJoin(s:DirUrlPrefix(simplify(s:GitDir(dir))), commit . simplify(file)))
2018-07-04 06:53:25 -04:00
endfunction
function! fugitive#resolve(url) abort
let url = fugitive#simplify(a:url)
if url =~? '^fugitive:'
return url
else
return resolve(url)
endif
endfunction
function! fugitive#getftime(url) abort
return s:PathInfo(a:url)[0]
endfunction
function! fugitive#getfsize(url) abort
let entry = s:PathInfo(a:url)
if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3])
let dir = s:DirCommitFile(a:url)[0]
2019-08-22 11:36:17 -04:00
let entry[4] = +s:ChompDefault(-1, [dir, 'cat-file', '-s', entry[3]])
2018-07-04 06:53:25 -04:00
endif
return entry[4]
endfunction
function! fugitive#getftype(url) abort
return get({'tree': 'dir', 'blob': 'file'}, s:PathInfo(a:url)[2], '')
endfunction
function! fugitive#filereadable(url) abort
return s:PathInfo(a:url)[2] ==# 'blob'
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#filewritable(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
2019-08-22 11:36:17 -04:00
if commit !~# '^\d$' || !filewritable(fugitive#Find('.git/index', dir))
2018-07-30 17:18:16 -04:00
return 0
endif
return s:PathInfo(a:url)[2] ==# 'blob' ? 1 : 2
endfunction
2018-07-04 06:53:25 -04:00
function! fugitive#isdirectory(url) abort
return s:PathInfo(a:url)[2] ==# 'tree'
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#getfperm(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
let perm = getfperm(dir)
let fperm = s:PathInfo(a:url)[1]
if fperm ==# '040000'
let fperm = '000755'
endif
if fperm !~# '[15]'
let perm = tr(perm, 'x', '-')
endif
if fperm !~# '[45]$'
let perm = tr(perm, 'rw', '--')
endif
if commit !~# '^\d$'
let perm = tr(perm, 'w', '-')
endif
return perm ==# '---------' ? '' : perm
endfunction
2021-06-23 05:57:12 -04:00
function! s:UpdateIndex(dir, info) abort
2020-04-25 21:56:16 -04:00
let info = join(a:info[0:-2]) . "\t" . a:info[-1] . "\n"
2021-10-11 05:30:43 -04:00
let [error, exec_error] = s:StdoutToFile('', [a:dir, 'update-index', '--index-info'], info)
2021-07-30 16:52:54 -04:00
return !exec_error ? '' : len(error) ? error : 'unknown update-index error'
2020-04-25 21:56:16 -04:00
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#setfperm(url, perm) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
let entry = s:PathInfo(a:url)
let perm = fugitive#getfperm(a:url)
if commit !~# '^\d$' || entry[2] !=# 'blob' ||
\ substitute(perm, 'x', '-', 'g') !=# substitute(a:perm, 'x', '-', 'g')
return -2
endif
2020-04-25 21:56:16 -04:00
let error = s:UpdateIndex(dir, [a:perm =~# 'x' ? '000755' : '000644', entry[3], commit, file[1:-1]])
return len(error) ? -1 : 0
2018-07-30 17:18:16 -04:00
endfunction
if !exists('s:blobdirs')
let s:blobdirs = {}
endif
function! s:BlobTemp(url) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if empty(file)
return ''
endif
2022-08-08 09:45:56 -04:00
let key = s:GitDir(dir)
if !has_key(s:blobdirs, key)
let s:blobdirs[key] = tempname()
2018-07-30 17:18:16 -04:00
endif
2022-08-08 09:45:56 -04:00
let tempfile = s:blobdirs[key] . '/' . commit . file
2018-07-30 17:18:16 -04:00
let tempparent = fnamemodify(tempfile, ':h')
if !isdirectory(tempparent)
call mkdir(tempparent, 'p')
2021-10-11 05:30:43 -04:00
elseif isdirectory(tempfile)
if commit =~# '^\d$' && has('patch-7.4.1107')
call delete(tempfile, 'rf')
else
return ''
endif
2018-07-30 17:18:16 -04:00
endif
if commit =~# '^\d$' || !filereadable(tempfile)
let rev = s:DirRev(a:url)[1]
2021-10-11 05:30:43 -04:00
let blob_or_filters = fugitive#GitVersion(2, 11) ? '--filters' : 'blob'
let exec_error = s:StdoutToFile(tempfile, [dir, 'cat-file', blob_or_filters, rev])[1]
2019-08-22 11:36:17 -04:00
if exec_error
2018-07-30 17:18:16 -04:00
call delete(tempfile)
return ''
endif
endif
2018-08-25 12:13:42 -04:00
return s:Resolve(tempfile)
2018-07-30 17:18:16 -04:00
endfunction
2018-07-04 06:53:25 -04:00
function! fugitive#readfile(url, ...) abort
let entry = s:PathInfo(a:url)
if entry[2] !=# 'blob'
return []
endif
2018-07-30 17:18:16 -04:00
let temp = s:BlobTemp(a:url)
if empty(temp)
return []
2018-07-04 06:53:25 -04:00
endif
2018-07-30 17:18:16 -04:00
return call('readfile', [temp] + a:000)
endfunction
function! fugitive#writefile(lines, url, ...) abort
let url = type(a:url) ==# type('') ? a:url : ''
let [dir, commit, file] = s:DirCommitFile(url)
let entry = s:PathInfo(url)
if commit =~# '^\d$' && entry[2] !=# 'tree'
2018-08-25 12:13:42 -04:00
let temp = tempname()
2018-07-30 17:18:16 -04:00
if a:0 && a:1 =~# 'a' && entry[2] ==# 'blob'
call writefile(fugitive#readfile(url, 'b'), temp, 'b')
endif
call call('writefile', [a:lines, temp] + a:000)
2021-10-11 05:30:43 -04:00
let hash = s:ChompDefault('', [dir, '--literal-pathspecs', 'hash-object', '-w', FugitiveGitPath(temp)])
let mode = entry[1] !=# '000000' ? entry[1] : '100644'
if hash =~# '^\x\{40,\}$'
2020-04-25 21:56:16 -04:00
let error = s:UpdateIndex(dir, [mode, hash, commit, file[1:-1]])
if empty(error)
2018-07-30 17:18:16 -04:00
return 0
endif
2018-07-04 06:53:25 -04:00
endif
endif
2018-07-30 17:18:16 -04:00
return call('writefile', [a:lines, a:url] + a:000)
2018-07-04 06:53:25 -04:00
endfunction
2018-08-25 12:13:42 -04:00
let s:globsubs = {
\ '/**/': '/\%([^./][^/]*/\)*',
\ '/**': '/\%([^./][^/]\+/\)*[^./][^/]*',
\ '**/': '[^/]*\%(/[^./][^/]*\)*',
\ '**': '.*',
\ '/*': '/[^/.][^/]*',
\ '*': '[^/]*',
\ '?': '[^/]'}
2018-07-04 06:53:25 -04:00
function! fugitive#glob(url, ...) abort
2022-08-08 09:45:56 -04:00
let [repo, commit, glob] = s:DirCommitFile(a:url)
let dirglob = s:GitDir(repo)
2018-07-04 06:53:25 -04:00
let append = matchstr(glob, '/*$')
let glob = substitute(glob, '/*$', '', '')
2019-05-17 10:09:13 -04:00
let pattern = '^' . substitute(glob, '/\=\*\*/\=\|/\=\*\|[.?\$]\|^^', '\=get(s:globsubs, submatch(0), "\\" . submatch(0))', 'g')[1:-1] . '$'
2018-07-04 06:53:25 -04:00
let results = []
for dir in dirglob =~# '[*?]' ? split(glob(dirglob), "\n") : [dirglob]
2019-08-22 11:36:17 -04:00
if empty(dir) || !get(g:, 'fugitive_file_api', 1) || !filereadable(fugitive#Find('.git/HEAD', dir))
2018-07-04 06:53:25 -04:00
continue
endif
let files = items(s:TreeInfo(dir, commit)[0])
if len(append)
call filter(files, 'v:val[1][2] ==# "tree"')
endif
call map(files, 'v:val[0]')
call filter(files, 'v:val =~# pattern')
2021-10-11 05:30:43 -04:00
let prepend = s:DirUrlPrefix(dir) . substitute(commit, '^:', '', '') . '/'
2018-07-04 06:53:25 -04:00
call sort(files)
2022-08-08 09:45:56 -04:00
call map(files, 's:VimSlash(s:PathJoin(prepend, v:val . append))')
2018-07-04 06:53:25 -04:00
call extend(results, files)
endfor
if a:0 > 1 && a:2
return results
else
return join(results, "\n")
endif
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#delete(url, ...) abort
let [dir, commit, file] = s:DirCommitFile(a:url)
if a:0 && len(a:1) || commit !~# '^\d$'
return -1
endif
let entry = s:PathInfo(a:url)
if entry[2] !=# 'blob'
return -1
endif
2020-04-25 21:56:16 -04:00
let error = s:UpdateIndex(dir, ['000000', '0000000000000000000000000000000000000000', commit, file[1:-1]])
return len(error) ? -1 : 0
2018-07-30 17:18:16 -04:00
endfunction
2018-08-25 12:13:42 -04:00
" Section: Completion
2019-08-22 11:36:17 -04:00
function! s:FilterEscape(items, ...) abort
let items = copy(a:items)
2022-08-08 09:45:56 -04:00
call map(items, 'fnameescape(v:val)')
if !a:0 || type(a:1) != type('')
let match = ''
else
let match = substitute(a:1, '^[+>]\|\\\@<![' . substitute(s:fnameescape, '\\', '', '') . ']', '\\&', 'g')
2019-08-22 11:36:17 -04:00
endif
2022-08-08 09:45:56 -04:00
let cmp = s:FileIgnoreCase(1) ? '==?' : '==#'
return filter(items, 'strpart(v:val, 0, strlen(match)) ' . cmp . ' match')
2019-08-22 11:36:17 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! s:GlobComplete(lead, pattern, ...) abort
2019-08-22 11:36:17 -04:00
if a:lead ==# '/'
return []
2018-06-14 06:31:12 -04:00
else
let results = glob(substitute(a:lead . a:pattern, '[\{}]', '\\&', 'g'), a:0 ? a:1 : 0, 1)
2018-06-14 06:31:12 -04:00
endif
2018-08-25 12:13:42 -04:00
call map(results, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val')
call map(results, 'v:val[ strlen(a:lead) : -1 ]')
return results
2018-06-14 06:31:12 -04:00
endfunction
2019-05-17 10:09:13 -04:00
function! fugitive#CompletePath(base, ...) abort
2020-04-25 21:56:16 -04:00
let dir = a:0 == 1 ? a:1 : a:0 >= 3 ? a:3 : s:Dir()
let stripped = matchstr(a:base, '^\%(:/:\=\|:(top)\|:(top,literal)\|:(literal,top)\)')
let base = strpart(a:base, len(stripped))
if len(stripped) || a:0 < 4
let root = s:Tree(dir)
else
let root = a:4
endif
if root !=# '/' && len(root)
let root .= '/'
endif
if empty(stripped)
let stripped = matchstr(a:base, '^\%(:(literal)\|:\)')
let base = strpart(a:base, len(stripped))
endif
2021-07-30 16:52:54 -04:00
if base =~# '^\.git/' && len(dir)
2018-08-25 12:13:42 -04:00
let pattern = s:gsub(base[5:-1], '/', '*&').'*'
2021-10-11 05:30:43 -04:00
let fdir = fugitive#Find('.git/', dir)
2021-07-30 16:52:54 -04:00
let matches = s:GlobComplete(fdir, pattern)
let cdir = fugitive#Find('.git/refs', dir)[0 : -5]
if len(cdir) && s:cpath(fdir) !=# s:cpath(cdir)
call extend(matches, s:GlobComplete(cdir, pattern))
2018-08-25 12:13:42 -04:00
endif
call s:Uniq(matches)
call map(matches, "'.git/' . v:val")
elseif base =~# '^\~/'
let matches = map(s:GlobComplete(expand('~/'), base[2:-1] . '*'), '"~/" . v:val')
2020-04-25 21:56:16 -04:00
elseif a:base =~# '^/\|^\a\+:\|^\.\.\=/'
2018-08-25 12:13:42 -04:00
let matches = s:GlobComplete('', base . '*')
2020-04-25 21:56:16 -04:00
elseif len(root)
let matches = s:GlobComplete(root, s:gsub(base, '/', '*&').'*')
2018-08-25 12:13:42 -04:00
else
let matches = []
endif
2020-04-25 21:56:16 -04:00
call map(matches, 's:fnameescape(s:Slash(stripped . v:val))')
2018-07-30 17:18:16 -04:00
return matches
2018-06-14 06:31:12 -04:00
endfunction
2019-05-17 10:09:13 -04:00
function! fugitive#PathComplete(...) abort
return call('fugitive#CompletePath', a:000)
endfunction
2019-11-16 10:28:42 -05:00
function! s:CompleteHeads(dir) abort
2020-04-25 21:56:16 -04:00
if empty(a:dir)
return []
endif
2019-11-16 10:28:42 -05:00
let dir = fugitive#Find('.git/', a:dir)
return sort(filter(['HEAD', 'FETCH_HEAD', 'ORIG_HEAD'] + s:merge_heads, 'filereadable(dir . v:val)')) +
2020-04-25 21:56:16 -04:00
\ sort(s:LinesError([a:dir, 'rev-parse', '--symbolic', '--branches', '--tags', '--remotes'])[0])
2019-11-16 10:28:42 -05:00
endfunction
2019-05-17 10:09:13 -04:00
function! fugitive#CompleteObject(base, ...) abort
2020-04-25 21:56:16 -04:00
let dir = a:0 == 1 ? a:1 : a:0 >= 3 ? a:3 : s:Dir()
let tree = s:Tree(dir)
2019-08-22 11:36:17 -04:00
let cwd = getcwd()
2018-08-25 12:13:42 -04:00
let subdir = ''
2020-04-25 21:56:16 -04:00
if len(tree) && s:cpath(tree . '/', cwd[0 : len(tree)])
let subdir = strpart(cwd, len(tree) + 1) . '/'
2018-08-25 12:13:42 -04:00
endif
2021-10-11 05:30:43 -04:00
let base = s:Expand(a:base)
2018-08-25 12:13:42 -04:00
2021-10-11 05:30:43 -04:00
if a:base =~# '^!\d*$' && base !~# '^!'
return [base]
elseif base =~# '^\.\=/\|^:(' || base !~# ':'
2018-07-30 17:18:16 -04:00
let results = []
2021-10-11 05:30:43 -04:00
if base =~# '^refs/'
2021-07-30 16:52:54 -04:00
let cdir = fugitive#Find('.git/refs', dir)[0 : -5]
2021-10-11 05:30:43 -04:00
let results += map(s:GlobComplete(cdir, base . '*'), 's:Slash(v:val)')
2019-11-30 07:06:56 -05:00
call map(results, 's:fnameescape(v:val)')
2021-10-11 05:30:43 -04:00
elseif base !~# '^\.\=/\|^:('
2019-11-16 10:28:42 -05:00
let heads = s:CompleteHeads(dir)
2019-08-22 11:36:17 -04:00
if filereadable(fugitive#Find('.git/refs/stash', dir))
2018-07-30 17:18:16 -04:00
let heads += ["stash"]
2019-08-22 11:36:17 -04:00
let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0])
2018-07-30 17:18:16 -04:00
endif
2021-10-28 15:48:21 -04:00
let results += s:FilterEscape(heads, fnameescape(base))
2018-06-14 06:31:12 -04:00
endif
2021-10-11 05:30:43 -04:00
let results += a:0 == 1 || a:0 >= 3 ? fugitive#CompletePath(base, 0, '', dir, a:0 >= 4 ? a:4 : tree) : fugitive#CompletePath(base)
2018-07-30 17:18:16 -04:00
return results
2018-06-14 06:31:12 -04:00
2021-10-11 05:30:43 -04:00
elseif base =~# '^:'
2019-08-22 11:36:17 -04:00
let entries = s:LinesError(['ls-files','--stage'], dir)[0]
2021-10-11 05:30:43 -04:00
if base =~# ':\./'
2018-08-25 12:13:42 -04:00
call map(entries, 'substitute(v:val, "\\M\t\\zs" . subdir, "./", "")')
endif
2018-07-30 17:18:16 -04:00
call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
2021-10-11 05:30:43 -04:00
if base !~# '^:[0-3]\%(:\|$\)'
2018-07-30 17:18:16 -04:00
call filter(entries,'v:val[1] == "0"')
call map(entries,'v:val[2:-1]')
endif
2018-06-14 06:31:12 -04:00
2018-07-30 17:18:16 -04:00
else
2021-10-11 05:30:43 -04:00
let parent = matchstr(base, '.*[:/]')
2020-04-25 21:56:16 -04:00
let entries = s:LinesError(['ls-tree', substitute(parent, ':\zs\./', '\=subdir', '')], dir)[0]
2018-07-30 17:18:16 -04:00
call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
2021-05-05 04:25:00 -04:00
call map(entries,'parent.s:sub(v:val,".*\t","")')
2018-07-30 17:18:16 -04:00
endif
2021-10-28 15:48:21 -04:00
return s:FilterEscape(entries, fnameescape(base))
2018-07-30 17:18:16 -04:00
endfunction
2018-06-14 06:31:12 -04:00
2019-08-22 11:36:17 -04:00
function! s:CompleteSub(subcommand, A, L, P, ...) abort
let pre = strpart(a:L, 0, a:P)
if pre =~# ' -- '
return fugitive#CompletePath(a:A)
elseif a:A =~# '^-' || a:A is# 0
2021-10-11 05:30:43 -04:00
return s:FilterEscape(split(s:ChompDefault('', [a:subcommand, '--git-completion-helper']), ' '), a:A)
2019-08-22 11:36:17 -04:00
elseif !a:0
return fugitive#CompleteObject(a:A, s:Dir())
elseif type(a:1) == type(function('tr'))
2020-04-25 21:56:16 -04:00
return call(a:1, [a:A, a:L, a:P] + (a:0 > 1 ? a:2 : []))
2019-08-22 11:36:17 -04:00
else
return s:FilterEscape(a:1, a:A)
endif
endfunction
2019-11-16 10:28:42 -05:00
function! s:CompleteRevision(A, L, P, ...) abort
2020-04-25 21:56:16 -04:00
return s:FilterEscape(s:CompleteHeads(a:0 ? a:1 : s:Dir()), a:A)
2019-08-22 11:36:17 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:CompleteRemote(A, L, P, ...) abort
let dir = a:0 ? a:1 : s:Dir()
2020-12-04 16:15:32 -05:00
let remote = matchstr(a:L, '\u\w*[! ] *.\{-\}\s\@<=\zs[^-[:space:]]\S*\ze ')
2019-08-22 11:36:17 -04:00
if !empty(remote)
2020-04-25 21:56:16 -04:00
let matches = s:LinesError([dir, 'ls-remote', remote])[0]
2019-08-22 11:36:17 -04:00
call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
else
2020-04-25 21:56:16 -04:00
let matches = s:LinesError([dir, 'remote'])[0]
2019-08-22 11:36:17 -04:00
endif
return s:FilterEscape(matches, a:A)
2019-05-17 10:09:13 -04:00
endfunction
2018-08-25 12:13:42 -04:00
" Section: Buffer auto-commands
2018-06-14 06:31:12 -04:00
2021-10-11 05:30:43 -04:00
augroup fugitive_dummy_events
autocmd!
autocmd User Fugitive* "
2022-08-08 09:45:56 -04:00
autocmd BufWritePre,FileWritePre,FileWritePost * "
autocmd BufNewFile * "
autocmd QuickfixCmdPre,QuickfixCmdPost * "
2021-10-11 05:30:43 -04:00
augroup END
2019-05-17 10:09:13 -04:00
function! s:ReplaceCmd(cmd) abort
2018-08-25 12:13:42 -04:00
let temp = tempname()
2021-10-11 05:30:43 -04:00
let [err, exec_error] = s:StdoutToFile(temp, a:cmd)
2019-08-22 11:36:17 -04:00
if exec_error
2021-10-11 05:30:43 -04:00
throw 'fugitive: ' . (len(err) ? substitute(err, "\n$", '', '') : 'unknown error running ' . string(a:cmd))
2018-07-30 17:18:16 -04:00
endif
2020-12-04 16:15:32 -05:00
setlocal noswapfile
2022-08-08 09:45:56 -04:00
silent exe 'lockmarks keepalt noautocmd 0read ++edit' s:fnameescape(temp)
2020-04-25 21:56:16 -04:00
if &foldenable && foldlevel('$') > 0
set nofoldenable
silent keepjumps $delete _
set foldenable
else
silent keepjumps $delete _
endif
2020-01-28 21:07:36 -05:00
call delete(temp)
2022-08-08 09:45:56 -04:00
if s:cpath(s:AbsoluteVimPath(bufnr('$')), temp)
silent! noautocmd execute bufnr('$') . 'bwipeout'
2020-01-28 21:07:36 -05:00
endif
2018-07-30 17:18:16 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:QueryLog(refspec, limit) abort
2022-08-08 09:45:56 -04:00
let lines = s:LinesError(['log', '-n', '' . a:limit, '--pretty=format:%h%x09%s'] + a:refspec + ['--'])[0]
2021-05-05 04:25:00 -04:00
call map(lines, 'split(v:val, "\t", 1)')
call map(lines, '{"type": "Log", "commit": v:val[0], "subject": join(v:val[1 : -1], "\t")}')
2019-01-08 05:11:54 -05:00
return lines
endfunction
function! s:FormatLog(dict) abort
return a:dict.commit . ' ' . a:dict.subject
endfunction
function! s:FormatRebase(dict) abort
return a:dict.status . ' ' . a:dict.commit . ' ' . a:dict.subject
endfunction
function! s:FormatFile(dict) abort
return a:dict.status . ' ' . a:dict.filename
endfunction
function! s:Format(val) abort
if type(a:val) == type({})
return s:Format{a:val.type}(a:val)
elseif type(a:val) == type([])
return map(copy(a:val), 's:Format(v:val)')
else
return '' . a:val
endif
endfunction
function! s:AddHeader(key, value) abort
if empty(a:value)
return
endif
let before = 1
while !empty(getline(before))
let before += 1
endwhile
call append(before - 1, [a:key . ':' . (len(a:value) ? ' ' . a:value : '')])
if before == 1 && line('$') == 2
2019-11-16 10:28:42 -05:00
silent keepjumps 2delete _
2019-01-08 05:11:54 -05:00
endif
endfunction
function! s:AddSection(label, lines, ...) abort
let note = a:0 ? a:1 : ''
if empty(a:lines) && empty(note)
return
endif
call append(line('$'), ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines))
endfunction
2022-08-08 09:45:56 -04:00
function! s:AddLogSection(label, refspec) abort
2021-10-11 05:30:43 -04:00
let limit = 256
2022-08-08 09:45:56 -04:00
let log = s:QueryLog(a:refspec, limit)
2021-10-11 05:30:43 -04:00
if empty(log)
return
elseif len(log) == limit
call remove(log, -1)
let label = a:label . ' (' . (limit - 1). '+)'
else
let label = a:label . ' (' . len(log) . ')'
endif
call append(line('$'), ['', label] + s:Format(log))
endfunction
2020-01-28 21:07:36 -05:00
let s:rebase_abbrevs = {
\ 'p': 'pick',
\ 'r': 'reword',
\ 'e': 'edit',
\ 's': 'squash',
\ 'f': 'fixup',
\ 'x': 'exec',
\ 'd': 'drop',
\ 'l': 'label',
\ 't': 'reset',
\ 'm': 'merge',
\ 'b': 'break',
\ }
2021-10-28 15:48:21 -04:00
function! fugitive#BufReadStatus(...) abort
2018-08-25 12:13:42 -04:00
let amatch = s:Slash(expand('%:p'))
2021-10-28 15:48:21 -04:00
unlet! b:fugitive_reltime b:fugitive_type
2018-07-30 17:18:16 -04:00
try
2022-08-08 09:45:56 -04:00
doautocmd BufReadPre
2020-01-28 21:07:36 -05:00
let config = fugitive#Config()
2022-08-08 09:45:56 -04:00
let cmd = [s:Dir()]
2021-10-28 15:48:21 -04:00
setlocal noreadonly modifiable nomodeline buftype=nowrite
2022-08-08 09:45:56 -04:00
if amatch !~# '^fugitive:' && s:cpath($GIT_INDEX_FILE !=# '' ? resolve(s:GitIndexFileEnv()) : fugitive#Find('.git/index')) !=# s:cpath(amatch)
2021-10-11 05:30:43 -04:00
let cmd += [{'env': {'GIT_INDEX_FILE': FugitiveGitPath(amatch)}}]
2018-07-30 17:18:16 -04:00
endif
2019-01-08 05:11:54 -05:00
2020-04-25 21:56:16 -04:00
if fugitive#GitVersion(2, 15)
call add(cmd, '--no-optional-locks')
endif
2019-11-16 10:28:42 -05:00
let b:fugitive_files = {'Staged': {}, 'Unstaged': {}}
2019-03-08 06:04:56 -05:00
let [staged, unstaged, untracked] = [[], [], []]
2019-11-16 10:28:42 -05:00
let props = {}
2021-05-05 04:25:00 -04:00
let pull = ''
if empty(s:Tree())
let branch = FugitiveHead(0)
let head = FugitiveHead(11)
elseif fugitive#GitVersion(2, 11)
2019-11-16 10:28:42 -05:00
let cmd += ['status', '--porcelain=v2', '-bz']
let [output, message, exec_error] = s:NullError(cmd)
if exec_error
throw 'fugitive: ' . message
2019-03-08 06:04:56 -05:00
endif
2019-11-16 10:28:42 -05:00
let i = 0
while i < len(output)
let line = output[i]
let prop = matchlist(line, '# \(\S\+\) \(.*\)')
if len(prop)
let props[prop[1]] = prop[2]
elseif line[0] ==# '?'
2021-07-30 16:52:54 -04:00
call add(untracked, {'type': 'File', 'status': line[0], 'filename': line[2:-1], 'relative': [line[2:-1]]})
2019-11-16 10:28:42 -05:00
elseif line[0] !=# '#'
if line[0] ==# 'u'
let file = matchstr(line, '^.\{37\} \x\{40,\} \x\{40,\} \x\{40,\} \zs.*$')
else
let file = matchstr(line, '^.\{30\} \x\{40,\} \x\{40,\} \zs.*$')
endif
if line[0] ==# '2'
let i += 1
let file = matchstr(file, ' \zs.*')
2021-07-30 16:52:54 -04:00
let relative = [file, output[i]]
2019-11-16 10:28:42 -05:00
else
2021-07-30 16:52:54 -04:00
let relative = [file]
2019-11-16 10:28:42 -05:00
endif
2021-07-30 16:52:54 -04:00
let filename = join(reverse(copy(relative)), ' -> ')
2019-11-16 10:28:42 -05:00
let sub = matchstr(line, '^[12u] .. \zs....')
if line[2] !=# '.'
2021-07-30 16:52:54 -04:00
call add(staged, {'type': 'File', 'status': line[2], 'filename': filename, 'relative': relative, 'submodule': sub})
2019-11-16 10:28:42 -05:00
endif
if line[3] !=# '.'
2021-05-05 04:25:00 -04:00
let sub = matchstr(line, '^[12u] .. \zs....')
2021-07-30 16:52:54 -04:00
call add(unstaged, {'type': 'File', 'status': get({'C':'M','M':'?','U':'?'}, matchstr(sub, 'S\.*\zs[CMU]'), line[3]), 'filename': file, 'relative': [file], 'submodule': sub})
2019-11-16 10:28:42 -05:00
endif
endif
2019-01-08 05:11:54 -05:00
let i += 1
2019-11-16 10:28:42 -05:00
endwhile
let branch = substitute(get(props, 'branch.head', '(unknown)'), '\C^(\%(detached\|unknown\))$', '', '')
if len(branch)
let head = branch
elseif has_key(props, 'branch.oid')
let head = props['branch.oid'][0:10]
else
let head = FugitiveHead(11)
2019-01-08 05:11:54 -05:00
endif
2019-11-16 10:28:42 -05:00
let pull = get(props, 'branch.upstream', '')
else " git < 2.11
let cmd += ['status', '--porcelain', '-bz']
let [output, message, exec_error] = s:NullError(cmd)
if exec_error
throw 'fugitive: ' . message
2019-01-08 05:11:54 -05:00
endif
2019-11-16 10:28:42 -05:00
while get(output, 0, '') =~# '^\l\+:'
call remove(output, 0)
endwhile
let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
if head =~# '\.\.\.'
let [head, pull] = split(head, '\.\.\.')
let branch = head
elseif head ==# 'HEAD' || empty(head)
let head = FugitiveHead(11)
let branch = ''
else
let branch = head
2019-01-08 05:11:54 -05:00
endif
2019-11-16 10:28:42 -05:00
let i = 0
while i < len(output)
let line = output[i]
let file = line[3:-1]
let i += 1
if line[2] !=# ' '
continue
endif
if line[0:1] =~# '[RC]'
2021-07-30 16:52:54 -04:00
let relative = [file, output[i]]
2019-11-16 10:28:42 -05:00
let i += 1
2021-07-30 16:52:54 -04:00
else
let relative = [file]
2019-11-16 10:28:42 -05:00
endif
2021-07-30 16:52:54 -04:00
let filename = join(reverse(copy(relative)), ' -> ')
2019-11-16 10:28:42 -05:00
if line[0] !~# '[ ?!#]'
2021-07-30 16:52:54 -04:00
call add(staged, {'type': 'File', 'status': line[0], 'filename': filename, 'relative': relative, 'submodule': ''})
2019-11-16 10:28:42 -05:00
endif
if line[0:1] ==# '??'
2021-07-30 16:52:54 -04:00
call add(untracked, {'type': 'File', 'status': line[1], 'filename': filename, 'relative': relative})
2019-11-16 10:28:42 -05:00
elseif line[1] !~# '[ !#]'
2021-07-30 16:52:54 -04:00
call add(unstaged, {'type': 'File', 'status': line[1], 'filename': file, 'relative': [file], 'submodule': ''})
2019-11-16 10:28:42 -05:00
endif
endwhile
endif
2019-03-08 06:04:56 -05:00
2021-10-11 05:30:43 -04:00
let diff = {'Staged': {'stdout': ['']}, 'Unstaged': {'stdout': ['']}}
if len(staged)
let diff['Staged'] =
2022-08-08 09:45:56 -04:00
\ fugitive#Execute(['-c', 'diff.suppressBlankEmpty=false', '-c', 'core.quotePath=false', 'diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached'], function('len'))
2021-10-11 05:30:43 -04:00
endif
if len(unstaged)
let diff['Unstaged'] =
2022-08-08 09:45:56 -04:00
\ fugitive#Execute(['-c', 'diff.suppressBlankEmpty=false', '-c', 'core.quotePath=false', 'diff', '--color=never', '--no-ext-diff', '--no-prefix'], function('len'))
2021-10-11 05:30:43 -04:00
endif
2019-03-08 06:04:56 -05:00
for dict in staged
2019-11-16 10:28:42 -05:00
let b:fugitive_files['Staged'][dict.filename] = dict
2019-03-08 06:04:56 -05:00
endfor
for dict in unstaged
2019-11-16 10:28:42 -05:00
let b:fugitive_files['Unstaged'][dict.filename] = dict
2019-03-08 06:04:56 -05:00
endfor
2019-01-08 05:11:54 -05:00
let pull_type = 'Pull'
if len(pull)
2021-05-31 16:30:26 -04:00
let rebase = FugitiveConfigGet('branch.' . branch . '.rebase', config)
2019-01-08 05:11:54 -05:00
if empty(rebase)
2021-05-31 16:30:26 -04:00
let rebase = FugitiveConfigGet('pull.rebase', config)
2019-01-08 05:11:54 -05:00
endif
2020-01-28 21:07:36 -05:00
if rebase =~# '^\%(true\|yes\|on\|1\|interactive\|merges\|preserve\)$'
2019-01-08 05:11:54 -05:00
let pull_type = 'Rebase'
elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
let pull_type = 'Merge'
endif
endif
2021-05-31 16:30:26 -04:00
let push_remote = FugitiveConfigGet('branch.' . branch . '.pushRemote', config)
2019-01-08 05:11:54 -05:00
if empty(push_remote)
2021-05-31 16:30:26 -04:00
let push_remote = FugitiveConfigGet('remote.pushDefault', config)
2019-01-08 05:11:54 -05:00
endif
2021-05-31 16:30:26 -04:00
let fetch_remote = FugitiveConfigGet('branch.' . branch . '.remote', config)
2020-01-28 21:07:36 -05:00
if empty(fetch_remote)
let fetch_remote = 'origin'
endif
if empty(push_remote)
let push_remote = fetch_remote
2019-01-08 05:11:54 -05:00
endif
2021-05-31 16:30:26 -04:00
let push_default = FugitiveConfigGet('push.default', config)
2020-01-28 21:07:36 -05:00
if empty(push_default)
let push_default = fugitive#GitVersion(2) ? 'simple' : 'matching'
2019-01-08 05:11:54 -05:00
endif
2020-01-28 21:07:36 -05:00
if push_default ==# 'upstream'
let push = pull
2018-07-30 17:18:16 -04:00
else
2020-01-28 21:07:36 -05:00
let push = len(branch) ? (push_remote ==# '.' ? '' : push_remote . '/') . branch : ''
2019-01-08 05:11:54 -05:00
endif
if isdirectory(fugitive#Find('.git/rebase-merge/'))
let rebasing_dir = fugitive#Find('.git/rebase-merge/')
elseif isdirectory(fugitive#Find('.git/rebase-apply/'))
let rebasing_dir = fugitive#Find('.git/rebase-apply/')
endif
let rebasing = []
let rebasing_head = 'detached HEAD'
if exists('rebasing_dir') && filereadable(rebasing_dir . 'git-rebase-todo')
let rebasing_head = substitute(readfile(rebasing_dir . 'head-name')[0], '\C^refs/heads/', '', '')
let len = 11
let lines = readfile(rebasing_dir . 'git-rebase-todo')
for line in lines
let hash = matchstr(line, '^[^a-z].*\s\zs[0-9a-f]\{4,\}\ze\.\.')
if len(hash)
let len = len(hash)
break
endif
endfor
if getfsize(rebasing_dir . 'done') > 0
let done = readfile(rebasing_dir . 'done')
call map(done, 'substitute(v:val, ''^\l\+\>'', "done", "")')
let done[-1] = substitute(done[-1], '^\l\+\>', 'stop', '')
let lines = done + lines
2018-07-30 17:18:16 -04:00
endif
2019-01-08 05:11:54 -05:00
call reverse(lines)
for line in lines
let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
if len(match) && match[1] !~# 'exec\|merge\|label'
call add(rebasing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': strpart(match[2], 0, len), 'subject': match[3]})
endif
endfor
2018-07-30 17:18:16 -04:00
endif
2019-01-08 05:11:54 -05:00
2019-08-22 11:36:17 -04:00
let b:fugitive_diff = diff
2021-10-28 15:48:21 -04:00
if get(a:, 1, v:cmdbang)
2021-10-11 05:30:43 -04:00
unlet! b:fugitive_expanded
endif
2019-01-08 05:11:54 -05:00
let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}})
let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}}
silent keepjumps %delete_
call s:AddHeader('Head', head)
call s:AddHeader(pull_type, pull)
if push !=# pull
call s:AddHeader('Push', push)
endif
2021-10-11 05:30:43 -04:00
if empty(s:Tree())
2022-08-08 09:45:56 -04:00
if get(fugitive#ConfigGetAll('core.bare', config), 0, '') !~# '^\%(false\|no|off\|0\|\)$'
2021-10-11 05:30:43 -04:00
call s:AddHeader('Bare', 'yes')
else
call s:AddHeader('Error', s:worktree_error)
endif
2019-11-30 07:06:56 -05:00
endif
2021-07-04 16:47:44 -04:00
if get(fugitive#ConfigGetAll('advice.statusHints', config), 0, 'true') !~# '^\%(false\|no|off\|0\|\)$'
2020-12-04 16:15:32 -05:00
call s:AddHeader('Help', 'g?')
endif
2019-01-08 05:11:54 -05:00
call s:AddSection('Rebasing ' . rebasing_head, rebasing)
2019-08-22 11:36:17 -04:00
call s:AddSection('Untracked', untracked)
2019-01-08 05:11:54 -05:00
call s:AddSection('Unstaged', unstaged)
let unstaged_end = len(unstaged) ? line('$') : 0
call s:AddSection('Staged', staged)
let staged_end = len(staged) ? line('$') : 0
2020-01-28 21:07:36 -05:00
2022-08-08 09:45:56 -04:00
if len(push) && !(push ==# pull && get(props, 'branch.ab') =~# '^+0 ')
call s:AddLogSection('Unpushed to ' . push, [push . '..' . head])
2020-01-28 21:07:36 -05:00
endif
if len(pull) && push !=# pull
2022-08-08 09:45:56 -04:00
call s:AddLogSection('Unpushed to ' . pull, [pull . '..' . head])
2020-01-28 21:07:36 -05:00
endif
2022-08-08 09:45:56 -04:00
if empty(pull) && empty(push) && empty(rebasing)
call s:AddLogSection('Unpushed to *', [head, '--not', '--remotes'])
endif
if len(push) && push !=# pull
call s:AddLogSection('Unpulled from ' . push, [head . '..' . push])
endif
if len(pull) && get(props, 'branch.ab') !~# ' -0$'
call s:AddLogSection('Unpulled from ' . pull, [head . '..' . pull])
2020-01-28 21:07:36 -05:00
endif
2019-01-08 05:11:54 -05:00
2019-03-08 06:04:56 -05:00
setlocal nomodified readonly noswapfile
2022-08-08 09:45:56 -04:00
doautocmd BufReadPost
2019-01-08 05:11:54 -05:00
setlocal nomodifiable
2018-07-30 17:18:16 -04:00
if &bufhidden ==# ''
setlocal bufhidden=delete
endif
2021-10-11 05:30:43 -04:00
if !exists('b:dispatch')
let b:dispatch = ':Git fetch --all'
endif
2018-07-30 17:18:16 -04:00
call fugitive#MapJumps()
2019-08-22 11:36:17 -04:00
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('n', 's', ":<C-U>execute <SID>Do('Stage',0)<CR>", '<silent>')
call s:Map('x', 's', ":<C-U>execute <SID>Do('Stage',1)<CR>", '<silent>')
call s:Map('n', 'u', ":<C-U>execute <SID>Do('Unstage',0)<CR>", '<silent>')
call s:Map('x', 'u', ":<C-U>execute <SID>Do('Unstage',1)<CR>", '<silent>')
2021-10-11 05:30:43 -04:00
call s:Map('n', 'U', ":<C-U>Git reset -q<CR>", '<silent>')
2019-08-22 11:36:17 -04:00
call s:MapMotion('gu', "exe <SID>StageJump(v:count, 'Untracked', 'Unstaged')")
call s:MapMotion('gU', "exe <SID>StageJump(v:count, 'Unstaged', 'Untracked')")
call s:MapMotion('gs', "exe <SID>StageJump(v:count, 'Staged')")
call s:MapMotion('gp', "exe <SID>StageJump(v:count, 'Unpushed')")
call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
2021-10-11 05:30:43 -04:00
call s:Map('n', 'C', ":echoerr 'fugitive: C has been removed in favor of cc'<CR>", '<silent><unique>')
2019-08-22 11:36:17 -04:00
call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
call s:Map('n', ">", ":<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>", '<silent>')
call s:Map('x', "=", ":<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
call s:Map('x', "<", ":<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
call s:Map('x', ">", ":<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
2021-10-11 05:30:43 -04:00
call s:Map('n', 'D', ":echoerr 'fugitive: D has been removed in favor of dd'<CR>", '<silent><unique>')
2019-08-22 11:36:17 -04:00
call s:Map('n', 'dd', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>", '<silent>')
call s:Map('n', 'dh', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
call s:Map('n', 'ds', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
call s:Map('n', 'dp', ":<C-U>execute <SID>StageDiffEdit()<CR>", '<silent>')
call s:Map('n', 'dv', ":<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>", '<silent>')
call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
call s:Map('n', 'P', ":<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>", '<silent>')
call s:Map('x', 'P', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
call s:Map('n', 'p', ":<C-U>if v:count<Bar>silent exe <SID>GF('pedit')<Bar>else<Bar>echoerr 'Use = for inline diff, P for :Git add/reset --patch, 1p for :pedit'<Bar>endif<CR>", '<silent>')
call s:Map('x', 'p', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
call s:Map('n', 'I', ":<C-U>execute <SID>StagePatch(line('.'),line('.'))<CR>", '<silent>')
call s:Map('x', 'I', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
call s:Map('n', 'gq', ":<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>", '<silent>')
call s:Map('n', 'R', ":echohl WarningMsg<Bar>echo 'Reloading is automatic. Use :e to force'<Bar>echohl NONE<CR>", '<silent>')
2021-10-11 05:30:43 -04:00
call s:Map('n', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent><unique>')
call s:Map('x', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent><unique>')
2019-08-22 11:36:17 -04:00
call s:Map('n', 'X', ":<C-U>execute <SID>StageDelete(line('.'), 0, v:count)<CR>", '<silent>')
call s:Map('x', 'X', ":<C-U>execute <SID>StageDelete(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
call s:Map('n', 'gI', ":<C-U>execute <SID>StageIgnore(line('.'), line('.'), v:count)<CR>", '<silent>')
call s:Map('x', 'gI', ":<C-U>execute <SID>StageIgnore(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
call s:Map('n', '.', ':<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>')
call s:Map('x', '.', ':<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>')
2019-03-08 06:04:56 -05:00
setlocal filetype=fugitive
2019-01-08 05:11:54 -05:00
for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']]
while len(getline(lnum))
let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*')
if has_key(expanded[section], filename)
call s:StageInline('show', lnum)
endif
let lnum -= 1
endwhile
endfor
2019-08-22 11:36:17 -04:00
let b:fugitive_reltime = reltime()
2021-10-11 05:30:43 -04:00
return s:DoAutocmd('User FugitiveIndex')
2018-07-30 17:18:16 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2021-10-28 15:48:21 -04:00
finally
let b:fugitive_type = 'index'
2018-07-30 17:18:16 -04:00
endtry
endfunction
function! fugitive#FileReadCmd(...) abort
let amatch = a:0 ? a:1 : expand('<amatch>')
let [dir, rev] = s:DirRev(amatch)
let line = a:0 > 1 ? a:2 : line("'[")
if empty(dir)
return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
endif
2019-08-22 11:36:17 -04:00
if rev !~# ':' && s:ChompDefault('', [dir, 'cat-file', '-t', rev]) =~# '^\%(commit\|tag\)$'
2021-10-11 05:30:43 -04:00
let cmd = [dir, 'log', '--pretty=format:%B', '-1', rev, '--']
2022-08-08 09:45:56 -04:00
elseif rev ==# ':'
let cmd = [dir, 'status', '--short']
2021-10-11 05:30:43 -04:00
else
let cmd = [dir, 'cat-file', '-p', rev, '--']
endif
let temp = tempname()
let [err, exec_error] = s:StdoutToFile(temp, cmd)
if exec_error
call delete(temp)
return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
2018-07-30 17:18:16 -04:00
else
2021-10-11 05:30:43 -04:00
return 'silent keepalt ' . line . 'read ' . s:fnameescape(temp) . '|call delete(' . string(temp) . ')'
2018-07-30 17:18:16 -04:00
endif
endfunction
function! fugitive#FileWriteCmd(...) abort
2021-08-04 09:52:11 -04:00
let temp = tempname()
2018-07-30 17:18:16 -04:00
let amatch = a:0 ? a:1 : expand('<amatch>')
let autype = a:0 > 1 ? 'Buf' : 'File'
if exists('#' . autype . 'WritePre')
2019-11-16 10:28:42 -05:00
execute s:DoAutocmd(autype . 'WritePre ' . s:fnameescape(amatch))
2018-07-30 17:18:16 -04:00
endif
try
let [dir, commit, file] = s:DirCommitFile(amatch)
if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$'))
return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch)
endif
2021-08-04 09:52:11 -04:00
silent execute "noautocmd keepalt '[,']write ".temp
2021-10-11 05:30:43 -04:00
let hash = s:TreeChomp([dir, '--literal-pathspecs', 'hash-object', '-w', '--', FugitiveGitPath(temp)])
let old_mode = matchstr(s:ChompDefault('', ['ls-files', '--stage', '.' . file], dir), '^\d\+')
2018-08-25 12:13:42 -04:00
if empty(old_mode)
2018-07-30 17:18:16 -04:00
let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
endif
2021-08-04 09:52:11 -04:00
let error = s:UpdateIndex(dir, [old_mode, hash, commit, file[1:-1]])
2020-04-25 21:56:16 -04:00
if empty(error)
2018-07-30 17:18:16 -04:00
setlocal nomodified
if exists('#' . autype . 'WritePost')
2019-11-16 10:28:42 -05:00
execute s:DoAutocmd(autype . 'WritePost ' . s:fnameescape(amatch))
2018-07-30 17:18:16 -04:00
endif
2021-10-11 05:30:43 -04:00
exe s:DoAutocmdChanged(dir)
2018-07-30 17:18:16 -04:00
return ''
else
return 'echoerr '.string('fugitive: '.error)
endif
2021-08-04 09:52:11 -04:00
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
2018-07-30 17:18:16 -04:00
finally
2021-08-04 09:52:11 -04:00
call delete(temp)
2018-07-30 17:18:16 -04:00
endtry
endfunction
function! fugitive#BufReadCmd(...) abort
let amatch = a:0 ? a:1 : expand('<amatch>')
try
let [dir, rev] = s:DirRev(amatch)
if empty(dir)
return 'echo "Invalid Fugitive URL"'
endif
2022-08-08 09:45:56 -04:00
call s:InitializeBuffer(dir)
if rev ==# ':'
return fugitive#BufReadStatus(v:cmdbang)
elseif rev =~# '^:\d$'
2018-07-30 17:18:16 -04:00
let b:fugitive_type = 'stage'
else
2021-10-11 05:30:43 -04:00
let r = fugitive#Execute([dir, 'cat-file', '-t', rev])
let b:fugitive_type = get(r.stdout, 0, '')
if r.exit_status && rev =~# '^:0'
let r = fugitive#Execute([dir, 'write-tree', '--prefix=' . rev[3:-1]])
let sha = get(r.stdout, 0, '')
let b:fugitive_type = 'tree'
2018-07-30 17:18:16 -04:00
endif
2021-10-11 05:30:43 -04:00
if r.exit_status
let error = substitute(join(r.stderr, "\n"), "\n*$", '', '')
2018-07-30 17:18:16 -04:00
unlet b:fugitive_type
2019-08-22 11:36:17 -04:00
setlocal noswapfile
if empty(&bufhidden)
setlocal bufhidden=delete
endif
2018-07-30 17:18:16 -04:00
if rev =~# '^:\d:'
2019-08-22 11:36:17 -04:00
let &l:readonly = !filewritable(fugitive#Find('.git/index', dir))
2022-08-08 09:45:56 -04:00
return 'doautocmd BufNewFile'
2018-07-30 17:18:16 -04:00
else
setlocal readonly nomodifiable
2022-08-08 09:45:56 -04:00
return 'doautocmd BufNewFile|echo ' . string(error)
2018-07-30 17:18:16 -04:00
endif
elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'")
endif
if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
endif
endif
if b:fugitive_type !=# 'blob'
setlocal nomodeline
endif
setlocal noreadonly modifiable
let pos = getpos('.')
silent keepjumps %delete_
setlocal endofline
2021-10-11 05:30:43 -04:00
let events = ['User FugitiveObject', 'User Fugitive' . substitute(b:fugitive_type, '^\l', '\u&', '')]
2018-07-30 17:18:16 -04:00
try
2021-05-31 16:30:26 -04:00
if b:fugitive_type !=# 'blob'
setlocal foldmarker=<<<<<<<<,>>>>>>>>
2021-05-05 04:25:00 -04:00
endif
2022-08-08 09:45:56 -04:00
exe s:DoAutocmd('BufReadPre')
2018-07-30 17:18:16 -04:00
if b:fugitive_type ==# 'tree'
let b:fugitive_display_format = b:fugitive_display_format % 2
if b:fugitive_display_format
call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev])
else
if !exists('sha')
2019-08-22 11:36:17 -04:00
let sha = s:TreeChomp(dir, 'rev-parse', '--verify', rev, '--')
2018-07-30 17:18:16 -04:00
endif
call s:ReplaceCmd([dir, 'show', '--no-color', sha])
endif
elseif b:fugitive_type ==# 'tag'
let b:fugitive_display_format = b:fugitive_display_format % 2
if b:fugitive_display_format
call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
else
call s:ReplaceCmd([dir, 'cat-file', '-p', rev])
endif
elseif b:fugitive_type ==# 'commit'
let b:fugitive_display_format = b:fugitive_display_format % 2
if b:fugitive_display_format
call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
else
2021-10-28 15:48:21 -04:00
call s:ReplaceCmd([dir, '-c', 'diff.noprefix=false', '-c', 'log.showRoot=false', 'show', '--no-color', '-m', '--first-parent', '--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b', rev])
2020-01-28 21:07:36 -05:00
keepjumps 1
2018-07-30 17:18:16 -04:00
keepjumps call search('^parent ')
if getline('.') ==# 'parent '
2020-12-04 16:15:32 -05:00
silent lockmarks keepjumps delete_
2018-07-30 17:18:16 -04:00
else
2018-09-24 20:40:17 -04:00
silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
2018-07-30 17:18:16 -04:00
endif
keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
if lnum
2020-12-04 16:15:32 -05:00
silent lockmarks keepjumps delete_
2018-07-30 17:18:16 -04:00
end
2018-11-01 06:03:42 -04:00
silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
2018-07-30 17:18:16 -04:00
keepjumps 1
endif
elseif b:fugitive_type ==# 'stage'
call s:ReplaceCmd([dir, 'ls-files', '--stage'])
elseif b:fugitive_type ==# 'blob'
2021-10-11 05:30:43 -04:00
let blob_or_filters = rev =~# ':' && fugitive#GitVersion(2, 11) ? '--filters' : 'blob'
call s:ReplaceCmd([dir, 'cat-file', blob_or_filters, rev])
2018-07-30 17:18:16 -04:00
endif
finally
keepjumps call setpos('.',pos)
setlocal nomodified noswapfile
2019-01-08 05:11:54 -05:00
let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
2021-10-11 05:30:43 -04:00
if modifiable
let events = ['User FugitiveStageBlob']
endif
2019-08-22 11:36:17 -04:00
let &l:readonly = !modifiable || !filewritable(fugitive#Find('.git/index', dir))
if empty(&bufhidden)
2018-07-30 17:18:16 -04:00
setlocal bufhidden=delete
endif
2019-01-08 05:11:54 -05:00
let &l:modifiable = modifiable
2018-07-30 17:18:16 -04:00
if b:fugitive_type !=# 'blob'
2021-05-05 04:25:00 -04:00
setlocal filetype=git
2019-08-22 11:36:17 -04:00
call s:Map('n', 'a', ":<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
call s:Map('n', 'i', ":<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2018-07-30 17:18:16 -04:00
endif
2019-03-08 06:04:56 -05:00
call fugitive#MapJumps()
2018-07-30 17:18:16 -04:00
endtry
2019-01-08 05:11:54 -05:00
setlocal modifiable
2020-01-28 21:07:36 -05:00
2022-08-08 09:45:56 -04:00
return s:DoAutocmd('BufReadPost') .
2021-10-11 05:30:43 -04:00
\ (modifiable ? '' : '|setl nomodifiable') . '|' .
\ call('s:DoAutocmd', events)
2018-07-30 17:18:16 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-07-30 17:18:16 -04:00
endtry
endfunction
function! fugitive#BufWriteCmd(...) abort
return fugitive#FileWriteCmd(a:0 ? a:1 : expand('<amatch>'), 1)
endfunction
function! fugitive#SourceCmd(...) abort
let amatch = a:0 ? a:1 : expand('<amatch>')
let temp = s:BlobTemp(amatch)
if empty(temp)
return 'noautocmd source ' . s:fnameescape(amatch)
endif
if !exists('g:virtual_scriptnames')
let g:virtual_scriptnames = {}
endif
let g:virtual_scriptnames[temp] = amatch
return 'source ' . s:fnameescape(temp)
endfunction
" Section: Temp files
if !exists('s:temp_files')
let s:temp_files = {}
endif
2019-08-22 11:36:17 -04:00
function! s:TempState(...) abort
2022-08-08 09:45:56 -04:00
return get(s:temp_files, s:cpath(s:AbsoluteVimPath(a:0 ? a:1 : -1)), {})
2019-08-22 11:36:17 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! fugitive#Result(...) abort
if !a:0 && exists('g:fugitive_event')
return get(g:, 'fugitive_result', {})
elseif !a:0 || type(a:1) == type('') && a:1 =~# '^-\=$'
return get(g:, '_fugitive_last_job', {})
elseif type(a:1) == type(0)
2022-08-08 09:45:56 -04:00
return s:TempState(a:1)
2021-05-05 04:25:00 -04:00
elseif type(a:1) == type('')
return s:TempState(a:1)
elseif type(a:1) == type({}) && has_key(a:1, 'file')
return s:TempState(a:1.file)
else
return {}
endif
endfunction
2021-10-11 05:30:43 -04:00
function! s:TempDotMap() abort
let cfile = s:cfile()
if empty(cfile)
if getline('.') =~# '^[*+] \+\f' && col('.') < 2
return matchstr(getline('.'), '^. \+\zs\f\+')
else
return expand('<cfile>')
endif
endif
let name = fugitive#Find(cfile[0])
let [dir, commit, file] = s:DirCommitFile(name)
if len(commit) && empty(file)
return commit
elseif s:cpath(s:Tree(), getcwd())
return fugitive#Path(name, "./")
else
return fugitive#Real(name)
endif
endfunction
2019-08-22 11:36:17 -04:00
function! s:TempReadPre(file) abort
2022-08-08 09:45:56 -04:00
let key = s:cpath(s:AbsoluteVimPath(a:file))
if has_key(s:temp_files, key)
let dict = s:temp_files[key]
2019-08-22 11:36:17 -04:00
setlocal nomodeline
2021-05-05 04:25:00 -04:00
if empty(&bufhidden)
setlocal bufhidden=delete
endif
2019-08-22 11:36:17 -04:00
setlocal buftype=nowrite
2020-01-28 21:07:36 -05:00
setlocal nomodifiable
2022-08-08 09:45:56 -04:00
call s:InitializeBuffer(dict)
2021-10-11 05:30:43 -04:00
if len(dict.git_dir)
2019-08-22 11:36:17 -04:00
call extend(b:, {'fugitive_type': 'temp'}, 'keep')
endif
endif
2022-08-08 09:45:56 -04:00
return ''
2019-08-22 11:36:17 -04:00
endfunction
function! s:TempReadPost(file) abort
2022-08-08 09:45:56 -04:00
let key = s:cpath(s:AbsoluteVimPath(a:file))
if has_key(s:temp_files, key)
let dict = s:temp_files[key]
2021-05-05 04:25:00 -04:00
if !has_key(dict, 'job')
setlocal nobuflisted
endif
if get(dict, 'filetype', '') ==# 'git'
call fugitive#MapJumps()
2021-10-11 05:30:43 -04:00
call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(<SID>TempDotMap())<CR><Home>")
call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(<SID>TempDotMap())<CR><Home>")
2021-05-05 04:25:00 -04:00
endif
if has_key(dict, 'filetype')
2021-10-11 05:30:43 -04:00
if dict.filetype ==# 'man' && has('nvim')
let b:man_sect = matchstr(getline(1), '^\w\+(\zs\d\+\ze)')
endif
2021-10-28 15:48:21 -04:00
if !get(g:, 'did_load_ftplugin') && dict.filetype ==# 'fugitiveblame'
call s:BlameMaps(0)
endif
2018-08-25 12:13:42 -04:00
let &l:filetype = dict.filetype
endif
2021-05-31 16:30:26 -04:00
setlocal foldmarker=<<<<<<<<,>>>>>>>>
2019-08-22 11:36:17 -04:00
if !&modifiable
call s:Map('n', 'gq', ":<C-U>bdelete<CR>", '<silent> <unique>')
2019-01-08 05:11:54 -05:00
endif
2018-08-25 12:13:42 -04:00
endif
2021-10-11 05:30:43 -04:00
return s:DoAutocmd('User FugitivePager')
2018-08-25 12:13:42 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! s:TempDelete(file) abort
2022-08-08 09:45:56 -04:00
let key = s:cpath(s:AbsoluteVimPath(a:file))
2021-05-05 04:25:00 -04:00
if has_key(s:temp_files, key) && !has_key(s:temp_files[key], 'job') && key !=# s:cpath(get(get(g:, '_fugitive_last_job', {}), 'file', ''))
call delete(a:file)
call remove(s:temp_files, key)
endif
return ''
endfunction
2018-07-30 17:18:16 -04:00
augroup fugitive_temp
autocmd!
2022-08-08 09:45:56 -04:00
autocmd BufReadPre * exe s:TempReadPre( +expand('<abuf>'))
autocmd BufReadPost * exe s:TempReadPost(+expand('<abuf>'))
autocmd BufWipeout * exe s:TempDelete( +expand('<abuf>'))
2018-07-30 17:18:16 -04:00
augroup END
2018-08-25 12:13:42 -04:00
" Section: :Git
2018-07-30 17:18:16 -04:00
2020-06-21 11:50:44 -04:00
function! s:AskPassArgs(dir) abort
2021-05-05 04:25:00 -04:00
if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) &&
2021-07-04 16:47:44 -04:00
\ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#ConfigGetAll('core.askpass', a:dir))
2022-08-08 09:45:56 -04:00
if s:executable(s:VimExecPath() . '/git-gui--askpass')
return ['-c', 'core.askPass=' . s:ExecPath()[0] . '/git-gui--askpass']
2020-06-21 11:50:44 -04:00
elseif s:executable('ssh-askpass')
return ['-c', 'core.askPass=ssh-askpass']
endif
endif
return []
endfunction
2021-05-05 04:25:00 -04:00
function! s:RunSave(state) abort
let s:temp_files[s:cpath(a:state.file)] = a:state
endfunction
function! s:RunFinished(state, ...) abort
if has_key(get(g:, '_fugitive_last_job', {}), 'file') && bufnr(g:_fugitive_last_job.file) < 0
exe s:TempDelete(remove(g:, '_fugitive_last_job').file)
endif
let g:_fugitive_last_job = a:state
let first = join(readfile(a:state.file, '', 2), "\n")
if get(a:state, 'filetype', '') ==# 'git' && first =~# '\<\([[:upper:][:digit:]_-]\+(\d\+)\).*\1'
let a:state.filetype = 'man'
endif
if !has_key(a:state, 'capture_bufnr')
return
endif
2021-10-11 05:30:43 -04:00
call fugitive#DidChange(a:state)
2021-05-05 04:25:00 -04:00
endfunction
function! s:RunEdit(state, tmp, job) abort
if get(a:state, 'request', '') !=# 'edit'
return 0
2020-06-21 11:50:44 -04:00
endif
2021-05-05 04:25:00 -04:00
call remove(a:state, 'request')
let sentinel = a:state.file . '.edit'
2021-07-30 16:52:54 -04:00
let file = FugitiveVimPath(readfile(sentinel, '', 1)[0])
2022-08-08 09:45:56 -04:00
try
if !&equalalways && a:state.mods !~# '\<tab\>' && 3 > (a:state.mods =~# '\<vert' ? winwidth(0) : winheight(0))
let noequalalways = 1
setglobal equalalways
endif
exe substitute(a:state.mods, '\<tab\>', '-tab', 'g') 'keepalt split' s:fnameescape(file)
finally
if exists('l:noequalalways')
setglobal noequalalways
endif
endtry
2021-05-05 04:25:00 -04:00
set bufhidden=wipe
2022-08-08 09:45:56 -04:00
call s:InitializeBuffer(a:state)
2021-10-11 05:30:43 -04:00
let bufnr = bufnr('')
let s:edit_jobs[bufnr] = [a:state, a:tmp, a:job, sentinel]
call fugitive#DidChange(a:state.git_dir)
if bufnr == bufnr('') && !exists('g:fugitive_event')
try
let g:fugitive_event = a:state.git_dir
let g:fugitive_result = a:state
exe s:DoAutocmd('User FugitiveEditor')
finally
unlet! g:fugitive_event g:fugitive_result
endtry
endif
2021-05-05 04:25:00 -04:00
return 1
2020-06-21 11:50:44 -04:00
endfunction
2021-05-05 04:25:00 -04:00
function! s:RunReceive(state, tmp, type, job, data, ...) abort
if a:type ==# 'err' || a:state.pty
let data = type(a:data) == type([]) ? join(a:data, "\n") : a:data
let data = a:tmp.escape . data
let escape = "\033]51;[^\007]*"
let a:tmp.escape = matchstr(data, escape . '$')
if len(a:tmp.escape)
let data = strpart(data, 0, len(data) - len(a:tmp.escape))
endif
let cmd = matchstr(data, escape . "\007")[5:-2]
let data = substitute(data, escape . "\007", '', 'g')
if cmd =~# '^fugitive:'
let a:state.request = strpart(cmd, 9)
endif
let lines = split(a:tmp.err . data, "\r\\=\n", 1)
let a:tmp.err = lines[-1]
let lines[-1] = ''
call map(lines, 'substitute(v:val, ".*\r", "", "")')
else
let lines = type(a:data) == type([]) ? a:data : split(a:data, "\n", 1)
if len(a:tmp.out)
let lines[0] = a:tmp.out . lines[0]
endif
let a:tmp.out = lines[-1]
let lines[-1] = ''
2020-01-28 21:07:36 -05:00
endif
2021-05-05 04:25:00 -04:00
call writefile(lines, a:state.file, 'ba')
if has_key(a:tmp, 'echo')
if !exists('l:data')
let data = type(a:data) == type([]) ? join(a:data, "\n") : a:data
endif
let a:tmp.echo .= data
2020-01-28 21:07:36 -05:00
endif
2021-05-05 04:25:00 -04:00
let line_count = a:tmp.line_count
let a:tmp.line_count += len(lines) - 1
if !has_key(a:state, 'capture_bufnr') || !bufloaded(a:state.capture_bufnr)
return
2020-01-28 21:07:36 -05:00
endif
2021-05-05 04:25:00 -04:00
call remove(lines, -1)
try
call setbufvar(a:state.capture_bufnr, '&modifiable', 1)
if !line_count && len(lines) > 1000
let first = remove(lines, 0, 999)
call setbufline(a:state.capture_bufnr, 1, first)
redraw
call setbufline(a:state.capture_bufnr, 1001, lines)
else
call setbufline(a:state.capture_bufnr, line_count + 1, lines)
endif
call setbufvar(a:state.capture_bufnr, '&modifiable', 0)
2022-08-08 09:45:56 -04:00
if !a:state.pager && getwinvar(bufwinid(a:state.capture_bufnr), '&previewwindow')
2021-05-05 04:25:00 -04:00
let winnr = bufwinnr(a:state.capture_bufnr)
if winnr > 0
let old_winnr = winnr()
exe 'noautocmd' winnr.'wincmd w'
$
exe 'noautocmd' old_winnr.'wincmd w'
endif
endif
catch
endtry
endfunction
function! s:RunExit(state, tmp, job, exit_status) abort
let a:state.exit_status = a:exit_status
if has_key(a:state, 'job')
return
2020-06-21 11:50:44 -04:00
endif
2021-05-05 04:25:00 -04:00
call s:RunFinished(a:state)
endfunction
function! s:RunClose(state, tmp, job, ...) abort
if a:0
call s:RunExit(a:state, a:tmp, a:job, a:1)
endif
let noeol = substitute(substitute(a:tmp.err, "\r$", '', ''), ".*\r", '', '') . a:tmp.out
call writefile([noeol], a:state.file, 'ba')
call remove(a:state, 'job')
if has_key(a:state, 'capture_bufnr') && bufloaded(a:state.capture_bufnr)
if len(noeol)
call setbufvar(a:state.capture_bufnr, '&modifiable', 1)
call setbufline(a:state.capture_bufnr, a:tmp.line_count + 1, [noeol])
call setbufvar(a:state.capture_bufnr, '&eol', 0)
call setbufvar(a:state.capture_bufnr, '&modifiable', 0)
endif
call setbufvar(a:state.capture_bufnr, '&modified', 0)
call setbufvar(a:state.capture_bufnr, '&buflisted', 0)
if a:state.filetype !=# getbufvar(a:state.capture_bufnr, '&filetype', '')
call setbufvar(a:state.capture_bufnr, '&filetype', a:state.filetype)
endif
endif
if !has_key(a:state, 'exit_status')
return
endif
call s:RunFinished(a:state)
2020-01-28 21:07:36 -05:00
endfunction
function! s:RunSend(job, str) abort
try
if type(a:job) == type(0)
call chansend(a:job, a:str)
else
call ch_sendraw(a:job, a:str)
endif
return len(a:str)
catch /^Vim\%((\a\+)\)\=:E90[06]:/
return 0
endtry
endfunction
2021-05-05 04:25:00 -04:00
function! s:RunCloseIn(job) abort
try
if type(a:job) ==# type(0)
call chanclose(a:job, 'stdin')
else
call ch_close_in(a:job)
endif
return 1
catch /^Vim\%((\a\+)\)\=:E90[06]:/
return 0
endtry
endfunction
function! s:RunEcho(tmp) abort
if !has_key(a:tmp, 'echo')
return
endif
let data = a:tmp.echo
let a:tmp.echo = matchstr(data, "[\r\n]\\+$")
if len(a:tmp.echo)
let data = strpart(data, 0, len(data) - len(a:tmp.echo))
endif
echon substitute(data, "\r\\ze\n", '', 'g')
endfunction
function! s:RunTick(job) abort
if type(a:job) == v:t_number
return jobwait([a:job], 1)[0] == -1
elseif type(a:job) == 8
2022-08-08 09:45:56 -04:00
let running = ch_status(a:job) !~# '^closed$\|^fail$' || job_status(a:job) ==# 'run'
2021-05-05 04:25:00 -04:00
sleep 1m
return running
endif
endfunction
2020-01-28 21:07:36 -05:00
if !exists('s:edit_jobs')
let s:edit_jobs = {}
endif
2021-05-05 04:25:00 -04:00
function! s:RunWait(state, tmp, job, ...) abort
if a:0 && filereadable(a:1)
call delete(a:1)
endif
2020-01-28 21:07:36 -05:00
try
2021-10-11 05:30:43 -04:00
if a:tmp.no_more && &more
let more = &more
let &more = 0
endif
2021-05-05 04:25:00 -04:00
while get(a:state, 'request', '') !=# 'edit' && s:RunTick(a:job)
call s:RunEcho(a:tmp)
2021-10-11 05:30:43 -04:00
if !get(a:tmp, 'closed_in')
2020-04-25 21:56:16 -04:00
let peek = getchar(1)
if peek != 0 && !(has('win32') && peek == 128)
let c = getchar()
let c = type(c) == type(0) ? nr2char(c) : c
2021-05-05 04:25:00 -04:00
if c ==# "\<C-D>" || c ==# "\<Esc>"
2021-10-11 05:30:43 -04:00
let a:tmp.closed_in = 1
2021-05-05 04:25:00 -04:00
let can_pedit = s:RunCloseIn(a:job) && exists('*setbufline')
for winnr in range(1, winnr('$'))
if getwinvar(winnr, '&previewwindow') && getbufvar(winbufnr(winnr), '&modified')
let can_pedit = 0
endif
endfor
if can_pedit
if has_key(a:tmp, 'echo')
call remove(a:tmp, 'echo')
endif
call writefile(['fugitive: aborting edit due to background operation.'], a:state.file . '.exit')
2021-10-11 05:30:43 -04:00
exe (&splitbelow ? 'botright' : 'topleft') 'silent pedit ++ff=unix' s:fnameescape(a:state.file)
2021-05-05 04:25:00 -04:00
let a:state.capture_bufnr = bufnr(a:state.file)
call setbufvar(a:state.capture_bufnr, '&modified', 1)
let finished = 0
redraw!
return ''
2020-04-25 21:56:16 -04:00
endif
else
call s:RunSend(a:job, c)
if !a:state.pty
echon c
endif
endif
2020-01-28 21:07:36 -05:00
endif
endif
endwhile
2021-05-05 04:25:00 -04:00
if !has_key(a:state, 'request') && has_key(a:state, 'job') && exists('*job_status') && job_status(a:job) ==# "dead"
throw 'fugitive: close callback did not fire; this should never happen'
endif
call s:RunEcho(a:tmp)
if has_key(a:tmp, 'echo')
let a:tmp.echo = substitute(a:tmp.echo, "^\r\\=\n", '', '')
echo
endif
let finished = !s:RunEdit(a:state, a:tmp, a:job)
2020-01-28 21:07:36 -05:00
finally
2021-10-11 05:30:43 -04:00
if exists('l:more')
let &more = more
endif
2021-05-05 04:25:00 -04:00
if !exists('finished')
2020-01-28 21:07:36 -05:00
try
2021-10-11 05:30:43 -04:00
if a:state.pty && !get(a:tmp, 'closed_in')
2020-01-28 21:07:36 -05:00
call s:RunSend(a:job, "\<C-C>")
elseif type(a:job) == type(0)
call jobstop(a:job)
else
call job_stop(a:job)
endif
catch /.*/
endtry
2021-05-05 04:25:00 -04:00
elseif finished
2021-10-11 05:30:43 -04:00
call fugitive#DidChange(a:state)
2020-01-28 21:07:36 -05:00
endif
endtry
return ''
endfunction
if !exists('s:resume_queue')
let s:resume_queue = []
endif
function! fugitive#Resume() abort
while len(s:resume_queue)
2022-08-08 09:45:56 -04:00
let enqueued = remove(s:resume_queue, 0)
if enqueued[2] isnot# ''
2021-05-05 04:25:00 -04:00
try
2022-08-08 09:45:56 -04:00
call call('s:RunWait', enqueued)
2021-05-05 04:25:00 -04:00
endtry
2020-04-25 21:56:16 -04:00
endif
2020-01-28 21:07:36 -05:00
endwhile
endfunction
function! s:RunBufDelete(bufnr) abort
2022-08-08 09:45:56 -04:00
let state = s:TempState(+a:bufnr)
2021-05-05 04:25:00 -04:00
if has_key(state, 'job')
try
if type(state.job) == type(0)
call jobstop(state.job)
else
call job_stop(state.job)
endif
catch
endtry
endif
2020-01-28 21:07:36 -05:00
if has_key(s:edit_jobs, a:bufnr) |
call add(s:resume_queue, remove(s:edit_jobs, a:bufnr))
2022-08-08 09:45:56 -04:00
call feedkeys("\<C-\>\<C-N>:redraw!|call delete(" . string(s:resume_queue[-1][0].file . '.edit') .
\ ")|call fugitive#Resume()|checktime\r", 'n')
2020-01-28 21:07:36 -05:00
endif
endfunction
augroup fugitive_job
autocmd!
2021-05-05 04:25:00 -04:00
autocmd BufDelete * call s:RunBufDelete(+expand('<abuf>'))
2020-01-28 21:07:36 -05:00
autocmd VimLeave *
\ for s:jobbuf in keys(s:edit_jobs) |
2021-05-05 04:25:00 -04:00
\ call writefile(['Aborting edit due to Vim exit.'], s:edit_jobs[s:jobbuf][0].file . '.exit') |
2020-04-25 21:56:16 -04:00
\ redraw! |
\ call call('s:RunWait', remove(s:edit_jobs, s:jobbuf)) |
2020-01-28 21:07:36 -05:00
\ endfor
augroup END
2021-10-11 05:30:43 -04:00
function! fugitive#CanPty() abort
return get(g:, 'fugitive_pty_debug_override',
\ has('unix') && !has('win32unix') && (has('patch-8.0.0744') || has('nvim')) && fugitive#GitVersion() !~# '\.windows\>')
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#PagerFor(argv, ...) abort
let args = a:argv
if empty(args)
return 0
elseif (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
return 1
endif
if args[0] ==# 'config' && (s:HasOpt(args, '-e', '--edit') ||
\ !s:HasOpt(args, '--list', '--get-all', '--get-regexp', '--get-urlmatch')) ||
\ args[0] =~# '^\%(tag\|branch\)$' && (
\ s:HasOpt(args, '--edit-description', '--unset-upstream', '-m', '-M', '--move', '-c', '-C', '--copy', '-d', '-D', '--delete') ||
\ len(filter(args[1:-1], 'v:val =~# "^[^-]\\|^--set-upstream-to="')) &&
\ !s:HasOpt(args, '--contains', '--no-contains', '--merged', '--no-merged', '--points-at'))
return 0
endif
let config = a:0 ? a:1 : fugitive#Config()
2021-07-04 16:47:44 -04:00
let value = get(fugitive#ConfigGetAll('pager.' . args[0], config), 0, -1)
2020-04-25 21:56:16 -04:00
if value =~# '^\%(true\|yes\|on\|1\)$'
return 1
elseif value =~# '^\%(false\|no|off\|0\|\)$'
return 0
elseif type(value) == type('')
return value
2021-06-23 05:57:12 -04:00
elseif args[0] =~# '^\%(branch\|config\|diff\|grep\|log\|range-diff\|shortlog\|show\|tag\|whatchanged\)$' ||
2020-04-25 21:56:16 -04:00
\ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
2021-06-23 05:57:12 -04:00
\ (args[0] ==# 'reflog' && get(args, 1, '') !~# '^\%(expire\|delete\|exists\)$') ||
2020-04-25 21:56:16 -04:00
\ (args[0] ==# 'am' && s:HasOpt(args, '--show-current-patch'))
return 1
else
return 0
endif
endfunction
2020-01-28 21:07:36 -05:00
let s:disable_colors = []
for s:colortype in ['advice', 'branch', 'diff', 'grep', 'interactive', 'pager', 'push', 'remote', 'showBranch', 'status', 'transport', 'ui']
call extend(s:disable_colors, ['-c', 'color.' . s:colortype . '=false'])
endfor
unlet s:colortype
2022-08-08 09:45:56 -04:00
function! fugitive#Command(line1, line2, range, bang, mods, arg, ...) abort
2021-05-05 04:25:00 -04:00
exe s:VersionCheck()
2022-08-08 09:45:56 -04:00
let dir = call('s:Dir', a:000)
2021-07-30 16:52:54 -04:00
if len(dir)
exe s:DirCheck(dir)
endif
2020-04-25 21:56:16 -04:00
let config = copy(fugitive#Config(dir))
2021-10-11 05:30:43 -04:00
let curwin = a:arg =~# '^++curwin\>' || !a:line2
let [args, after] = s:SplitExpandChain(substitute(a:arg, '^++curwin\>\s*', '', ''), s:Tree(dir))
2020-04-25 21:56:16 -04:00
let flags = []
let pager = -1
2021-10-11 05:30:43 -04:00
let explicit_pathspec_option = 0
2020-04-25 21:56:16 -04:00
while len(args)
if args[0] ==# '-c' && len(args) > 1
call extend(flags, remove(args, 0, 1))
elseif args[0] =~# '^-p$\|^--paginate$'
2021-10-11 05:30:43 -04:00
let pager = 2
2020-04-25 21:56:16 -04:00
call remove(args, 0)
elseif args[0] =~# '^-P$\|^--no-pager$'
let pager = 0
call remove(args, 0)
2021-10-11 05:30:43 -04:00
elseif args[0] =~# '^--\%([[:lower:]-]\+-pathspecs\)$'
let explicit_pathspec_option = 1
call add(flags, remove(args, 0))
elseif args[0] =~# '^\%(--no-optional-locks\)$'
2020-04-25 21:56:16 -04:00
call add(flags, remove(args, 0))
elseif args[0] =~# '^-C$\|^--\%(exec-path=\|git-dir=\|work-tree=\|bare$\)'
return 'echoerr ' . string('fugitive: ' . args[0] . ' is not supported')
else
break
endif
endwhile
2021-10-11 05:30:43 -04:00
if !explicit_pathspec_option
call insert(flags, '--no-literal-pathspecs')
endif
let no_pager = pager is# 0
if no_pager
2020-04-25 21:56:16 -04:00
call add(flags, '--no-pager')
endif
2021-10-11 05:30:43 -04:00
let env = {}
let i = 0
while i < len(flags) - 1
if flags[i] ==# '-c'
let i += 1
let config_name = tolower(matchstr(flags[i], '^[^=]\+'))
if has_key(s:prepare_env, config_name) && flags[i] =~# '=.'
let env[s:prepare_env[config_name]] = matchstr(flags[i], '=\zs.*')
endif
if flags[i] =~# '='
let config[config_name] = [matchstr(flags[i], '=\zs.*')]
else
let config[config_name] = [1]
endif
endif
let i += 1
endwhile
let options = {'git': s:UserCommandList(), 'git_dir': s:GitDir(dir), 'flags': flags, 'curwin': curwin}
2020-04-25 21:56:16 -04:00
if empty(args) && pager is# -1
2021-10-11 05:30:43 -04:00
let cmd = s:StatusCommand(a:line1, a:line2, a:range, curwin ? 0 : a:line2, a:bang, a:mods, '', '', [], options)
2019-08-22 11:36:17 -04:00
return (empty(cmd) ? 'exe' : cmd) . after
endif
2021-05-31 16:30:26 -04:00
let alias = FugitiveConfigGet('alias.' . get(args, 0, ''), config)
2022-08-08 09:45:56 -04:00
if get(args, 1, '') !=# '--help' && alias !~# '^$\|^!\|[\"'']' && !filereadable(s:VimExecPath() . '/git-' . args[0])
\ && !(has('win32') && filereadable(s:VimExecPath() . '/git-' . args[0] . '.exe'))
2019-08-22 11:36:17 -04:00
call remove(args, 0)
call extend(args, split(alias, '\s\+'), 'keep')
endif
2020-04-25 21:56:16 -04:00
let name = substitute(get(args, 0, ''), '\%(^\|-\)\(\l\)', '\u\1', 'g')
2021-06-23 05:57:12 -04:00
if pager is# -1 && name =~# '^\a\+$' && exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
2019-08-22 11:36:17 -04:00
try
2021-05-05 04:25:00 -04:00
let overrides = s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, extend({'subcommand': args[0], 'subcommand_args': args[1:-1]}, options))
2020-04-25 21:56:16 -04:00
if type(overrides) == type('')
return 'exe ' . string(overrides) . after
2020-01-07 07:45:07 -05:00
endif
2020-04-25 21:56:16 -04:00
let args = [get(overrides, 'command', args[0])] + get(overrides, 'insert_args', []) + args[1:-1]
2019-08-22 11:36:17 -04:00
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
2020-01-28 21:07:36 -05:00
else
2020-04-25 21:56:16 -04:00
let overrides = {}
2018-06-14 06:31:12 -04:00
endif
2021-10-11 05:30:43 -04:00
call extend(env, get(overrides, 'env', {}))
call s:PrepareEnv(env, dir)
if pager is# -1
2020-04-25 21:56:16 -04:00
let pager = fugitive#PagerFor(args, config)
endif
2021-10-11 05:30:43 -04:00
let wants_terminal = type(pager) ==# type('') ||
\ (s:HasOpt(args, ['add', 'checkout', 'commit', 'reset', 'restore', 'stage', 'stash'], '-p', '--patch') ||
\ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive')) && pager is# 0
if wants_terminal
2019-08-22 11:36:17 -04:00
let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
2022-08-08 09:45:56 -04:00
let assign = len(dir) ? "|call FugitiveDetect({'git_dir':" . string(options.git_dir) . '})' : ''
2021-10-11 05:30:43 -04:00
let argv = s:UserCommandList(options) + args
let term_opts = len(env) ? {'env': env} : {}
2019-08-22 11:36:17 -04:00
if has('nvim')
2021-05-05 04:25:00 -04:00
call fugitive#Autowrite()
2021-10-11 05:30:43 -04:00
return mods . (curwin ? 'enew' : 'new') . '|call termopen(' . string(argv) . ', ' . string(term_opts) . ')' . assign . '|startinsert' . after
elseif exists('*term_start')
2021-05-05 04:25:00 -04:00
call fugitive#Autowrite()
2021-10-11 05:30:43 -04:00
if curwin
let term_opts.curwin = 1
endif
return mods . 'call term_start(' . string(argv) . ', ' . string(term_opts) . ')' . assign . after
2018-06-14 06:31:12 -04:00
endif
2018-07-30 17:18:16 -04:00
endif
2021-05-05 04:25:00 -04:00
let state = {
2021-10-11 05:30:43 -04:00
\ 'git': options.git,
2021-05-05 04:25:00 -04:00
\ 'flags': flags,
\ 'args': args,
2021-10-11 05:30:43 -04:00
\ 'git_dir': options.git_dir,
2021-05-05 04:25:00 -04:00
\ 'cwd': s:UserCommandCwd(dir),
\ 'filetype': 'git',
\ 'mods': s:Mods(a:mods),
\ 'file': s:Resolve(tempname())}
2021-10-11 05:30:43 -04:00
let allow_pty = 1
let after_edit = ''
let stream = 0
if a:bang && pager isnot# 2
2022-08-08 09:45:56 -04:00
let state.pager = pager
2021-10-11 05:30:43 -04:00
let pager = 1
let stream = exists('*setbufline')
let do_edit = substitute(s:Mods(a:mods, 'Edge'), '\<tab\>', '-tab', 'g') . 'pedit!'
elseif pager
let allow_pty = 0
if pager is# 2 && a:bang && a:line2 >= 0
let [do_edit, after_edit] = s:ReadPrepare(a:line1, a:line2, a:range, a:mods)
elseif pager is# 2 && a:bang
let do_edit = s:Mods(a:mods) . 'pedit'
elseif !curwin
let do_edit = s:Mods(a:mods) . 'split'
else
let do_edit = s:Mods(a:mods) . 'edit'
call s:BlurStatus()
endif
2021-05-05 04:25:00 -04:00
call extend(env, {'COLUMNS': '' . get(g:, 'fugitive_columns', 80)}, 'keep')
endif
2021-10-11 05:30:43 -04:00
if s:run_jobs
call extend(env, {'COLUMNS': '' . (&columns - 1)}, 'keep')
let state.pty = allow_pty && fugitive#CanPty()
2020-01-28 21:07:36 -05:00
if !state.pty
let args = s:AskPassArgs(dir) + args
endif
2021-05-05 04:25:00 -04:00
let tmp = {
2021-10-11 05:30:43 -04:00
\ 'no_more': no_pager || get(overrides, 'no_more'),
2021-05-05 04:25:00 -04:00
\ 'line_count': 0,
\ 'err': '',
\ 'out': '',
\ 'escape': ''}
let env.FUGITIVE = state.file
2020-01-28 21:07:36 -05:00
let editor = 'sh ' . s:TempScript(
2021-05-05 04:25:00 -04:00
\ '[ -f "$FUGITIVE.exit" ] && cat "$FUGITIVE.exit" >&2 && exit 1',
\ 'echo "$1" > "$FUGITIVE.edit"',
\ 'printf "\033]51;fugitive:edit\007" >&2',
\ 'while [ -f "$FUGITIVE.edit" -a ! -f "$FUGITIVE.exit" ]; do sleep 0.05 2>/dev/null || sleep 1; done',
2020-01-28 21:07:36 -05:00
\ 'exit 0')
call extend(env, {
\ 'NO_COLOR': '1',
\ 'GIT_EDITOR': editor,
\ 'GIT_SEQUENCE_EDITOR': editor,
\ 'GIT_PAGER': 'cat',
\ 'PAGER': 'cat'}, 'keep')
2021-10-11 05:30:43 -04:00
if len($GPG_TTY) && !has_key(env, 'GPG_TTY')
let env.GPG_TTY = ''
let did_override_gpg_tty = 1
endif
if stream
call writefile(['fugitive: aborting edit due to background operation.'], state.file . '.exit')
elseif pager
call writefile(['fugitive: aborting edit due to use of pager.'], state.file . '.exit')
let after = '|' . do_edit . ' ' . s:fnameescape(state.file) . after_edit . after
else
let env.GIT_MERGE_AUTOEDIT = '1'
let tmp.echo = ''
endif
2020-04-25 21:56:16 -04:00
let args = s:disable_colors + flags + ['-c', 'advice.waitingForEditor=false'] + args
2021-10-11 05:30:43 -04:00
let argv = s:UserCommandList({'git': options.git, 'git_dir': options.git_dir}) + args
2020-04-25 21:56:16 -04:00
let [argv, jobopts] = s:JobOpts(argv, env)
2021-05-05 04:25:00 -04:00
call fugitive#Autowrite()
call writefile([], state.file, 'b')
call s:RunSave(state)
2021-10-11 05:30:43 -04:00
if has_key(tmp, 'echo')
echo ""
endif
2022-08-08 09:45:56 -04:00
if exists('*ch_close_in')
2020-04-25 21:56:16 -04:00
call extend(jobopts, {
2020-01-28 21:07:36 -05:00
\ 'mode': 'raw',
2021-05-05 04:25:00 -04:00
\ 'out_cb': function('s:RunReceive', [state, tmp, 'out']),
\ 'err_cb': function('s:RunReceive', [state, tmp, 'err']),
\ 'close_cb': function('s:RunClose', [state, tmp]),
\ 'exit_cb': function('s:RunExit', [state, tmp]),
2020-04-25 21:56:16 -04:00
\ })
2020-01-28 21:07:36 -05:00
if state.pty
let jobopts.pty = 1
endif
let job = job_start(argv, jobopts)
else
2020-04-25 21:56:16 -04:00
let job = jobstart(argv, extend(jobopts, {
2020-01-28 21:07:36 -05:00
\ 'pty': state.pty,
\ 'TERM': 'dumb',
2021-05-05 04:25:00 -04:00
\ 'on_stdout': function('s:RunReceive', [state, tmp, 'out']),
\ 'on_stderr': function('s:RunReceive', [state, tmp, 'err']),
\ 'on_exit': function('s:RunClose', [state, tmp]),
2020-04-25 21:56:16 -04:00
\ }))
2020-01-28 21:07:36 -05:00
endif
let state.job = job
2021-10-11 05:30:43 -04:00
if pager
let tmp.closed_in = 1
call s:RunCloseIn(job)
endif
if stream
exe 'silent' do_edit '++ff=unix' s:fnameescape(state.file)
let state.capture_bufnr = bufnr(state.file)
call setbufvar(state.capture_bufnr, '&modified', 1)
return (after_edit . after)[1:-1]
endif
2021-05-05 04:25:00 -04:00
call add(s:resume_queue, [state, tmp, job])
2022-08-08 09:45:56 -04:00
return 'call fugitive#Resume()|checktime' . after
2021-10-11 05:30:43 -04:00
elseif pager
2021-05-05 04:25:00 -04:00
let pre = s:BuildEnvPrefix(env)
2021-06-23 05:57:12 -04:00
try
if exists('+guioptions') && &guioptions =~# '!'
let guioptions = &guioptions
set guioptions-=!
endif
2021-10-11 05:30:43 -04:00
silent! execute '!' . escape(pre . s:shellesc(s:UserCommandList(options) + s:disable_colors + flags + ['--no-pager'] + args), '!#%') .
2021-06-23 05:57:12 -04:00
\ (&shell =~# 'csh' ? ' >& ' . s:shellesc(state.file) : ' > ' . s:shellesc(state.file) . ' 2>&1')
let state.exit_status = v:shell_error
finally
if exists('guioptions')
let &guioptions = guioptions
endif
endtry
2021-05-05 04:25:00 -04:00
redraw!
call s:RunSave(state)
call s:RunFinished(state)
2021-10-11 05:30:43 -04:00
return do_edit . ' ' . s:fnameescape(state.file) . after_edit .
\ '|call fugitive#DidChange(fugitive#Result(' . string(state.file) . '))' . after
2020-04-25 21:56:16 -04:00
elseif has('win32')
return 'echoerr ' . string('fugitive: Vim 8 with job support required to use :Git on Windows')
elseif has('gui_running')
return 'echoerr ' . string('fugitive: Vim 8 with job support required to use :Git in GVim')
2020-01-28 21:07:36 -05:00
else
2021-10-11 05:30:43 -04:00
if !explicit_pathspec_option && get(options.flags, 0, '') ==# '--no-literal-pathspecs'
call remove(options.flags, 0)
endif
if exists('l:did_override_gpg_tty')
call remove(env, 'GPG_TTY')
endif
let cmd = s:BuildEnvPrefix(env) . s:shellesc(s:UserCommandList(options) + args)
let after = '|call fugitive#DidChange(' . string(dir) . ')' . after
if !wants_terminal && (no_pager || index(['add', 'clean', 'reset', 'restore', 'stage'], get(args, 0, '')) >= 0 || s:HasOpt(args, ['checkout'], '-q', '--quiet', '--no-progress'))
let output = substitute(s:SystemError(cmd)[0], "\n$", '', '')
if len(output)
try
if &more && no_pager
let more = 1
set nomore
endif
echo substitute(output, "\n$", "", "")
finally
if exists('l:more')
set more
endif
endtry
endif
2022-08-08 09:45:56 -04:00
return 'checktime' . after
2021-10-11 05:30:43 -04:00
else
return 'exe ' . string('noautocmd !' . escape(cmd, '!#%')) . after
endif
2019-08-22 11:36:17 -04:00
endif
2018-07-30 17:18:16 -04:00
endfunction
let s:exec_paths = {}
2019-08-22 11:36:17 -04:00
function! s:ExecPath() abort
2021-05-31 16:30:26 -04:00
let git = s:GitShellCmd()
if !has_key(s:exec_paths, git)
2022-08-08 09:45:56 -04:00
let path = get(s:JobExecute(s:GitCmd() + ['--exec-path'], {}, [], [], {}).stdout, 0, '')
let s:exec_paths[git] = [path, FugitiveVimPath(path)]
2018-06-14 06:31:12 -04:00
endif
2021-05-31 16:30:26 -04:00
return s:exec_paths[git]
2019-08-22 11:36:17 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:VimExecPath() abort
return s:ExecPath()[1]
endfunction
2021-05-05 04:25:00 -04:00
let s:subcommands_before_2_5 = [
\ 'add', 'am', 'apply', 'archive', 'bisect', 'blame', 'branch', 'bundle',
\ 'checkout', 'cherry', 'cherry-pick', 'citool', 'clean', 'clone', 'commit', 'config',
\ 'describe', 'diff', 'difftool', 'fetch', 'format-patch', 'fsck',
\ 'gc', 'grep', 'gui', 'help', 'init', 'instaweb', 'log',
\ 'merge', 'mergetool', 'mv', 'notes', 'pull', 'push',
\ 'rebase', 'reflog', 'remote', 'repack', 'replace', 'request-pull', 'reset', 'revert', 'rm',
\ 'send-email', 'shortlog', 'show', 'show-branch', 'stash', 'stage', 'status', 'submodule',
\ 'tag', 'whatchanged',
\ ]
let s:path_subcommands = {}
function! s:CompletableSubcommands(dir) abort
2022-08-08 09:45:56 -04:00
let c_exec_path = s:cpath(s:VimExecPath())
2021-05-05 04:25:00 -04:00
if !has_key(s:path_subcommands, c_exec_path)
if fugitive#GitVersion(2, 18)
2021-10-11 05:30:43 -04:00
let [lines, exec_error] = s:LinesError([a:dir, '--list-cmds=list-mainporcelain,nohelpers,list-complete'])
2021-05-05 04:25:00 -04:00
call filter(lines, 'v:val =~# "^\\S\\+$"')
if !exec_error && len(lines)
let s:path_subcommands[c_exec_path] = lines
else
let s:path_subcommands[c_exec_path] = s:subcommands_before_2_5 +
\ ['maintenance', 'prune', 'range-diff', 'restore', 'sparse-checkout', 'switch', 'worktree']
endif
else
let s:path_subcommands[c_exec_path] = s:subcommands_before_2_5 +
\ (fugitive#GitVersion(2, 5) ? ['worktree'] : [])
endif
endif
let commands = copy(s:path_subcommands[c_exec_path])
for path in split($PATH, has('win32') ? ';' : ':')
if path !~# '^/\|^\a:[\\/]'
continue
endif
let cpath = s:cpath(path)
if !has_key(s:path_subcommands, cpath)
let s:path_subcommands[cpath] = filter(map(s:GlobComplete(path.'/git-', '*', 1),'substitute(v:val,"\\.exe$","","")'), 'v:val !~# "--\\|/"')
endif
call extend(commands, s:path_subcommands[cpath])
endfor
2021-07-04 16:47:44 -04:00
call extend(commands, keys(fugitive#ConfigGetRegexp('^alias\.\zs[^.]\+$', a:dir)))
2021-05-05 04:25:00 -04:00
let configured = split(FugitiveConfigGet('completion.commands', a:dir), '\s\+')
let rejected = {}
for command in configured
if command =~# '^-.'
let rejected[strpart(command, 1)] = 1
endif
endfor
call filter(configured, 'v:val !~# "^-"')
let results = filter(sort(commands + configured), '!has_key(rejected, v:val)')
if exists('*uniq')
return uniq(results)
else
let i = 1
while i < len(results)
if results[i] ==# results[i-1]
call remove(results, i)
else
let i += 1
endif
endwhile
return results
2018-06-14 06:31:12 -04:00
endif
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#Complete(lead, ...) abort
2021-10-11 05:30:43 -04:00
let dir = a:0 == 1 ? a:1 : a:0 >= 3 ? s:Dir(a:3) : s:Dir()
2020-04-25 21:56:16 -04:00
let root = a:0 >= 4 ? a:4 : s:Tree(s:Dir())
2019-05-17 10:09:13 -04:00
let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
2021-10-11 05:30:43 -04:00
let subcmd = matchstr(pre, '\u\w*[! ] *\%(\%(++\S\+\|--\S\+-pathspecs\|-c\s\+\S\+\)\s\+\)*\zs[[:alnum:]][[:alnum:]-]*\ze ')
if empty(subcmd) && a:lead =~# '^+'
let results = ['++curwin']
elseif empty(subcmd) && a:lead =~# '^-'
let results = ['--literal-pathspecs', '--no-literal-pathspecs', '--glob-pathspecs', '--noglob-pathspecs', '--icase-pathspecs', '--no-optional-locks']
elseif empty(subcmd)
2021-05-05 04:25:00 -04:00
let results = s:CompletableSubcommands(dir)
2022-08-08 09:45:56 -04:00
elseif a:0 ==# 2 && subcmd =~# '^\%(commit\|revert\|push\|fetch\|pull\|merge\|rebase\|bisect\)$'
2020-04-25 21:56:16 -04:00
let cmdline = substitute(a:1, '\u\w*\([! ] *\)' . subcmd, 'G' . subcmd, '')
let caps_subcmd = substitute(subcmd, '\%(^\|-\)\l', '\u&', 'g')
return fugitive#{caps_subcmd}Complete(a:lead, cmdline, a:2 + len(cmdline) - len(a:1), dir, root)
2018-07-30 17:18:16 -04:00
elseif pre =~# ' -- '
2020-04-25 21:56:16 -04:00
return fugitive#CompletePath(a:lead, a:1, a:2, dir, root)
2019-08-22 11:36:17 -04:00
elseif a:lead =~# '^-'
2021-10-11 05:30:43 -04:00
let results = split(s:ChompDefault('', [dir, subcmd, '--git-completion-helper']), ' ')
2018-06-14 06:31:12 -04:00
else
2020-04-25 21:56:16 -04:00
return fugitive#CompleteObject(a:lead, a:1, a:2, dir, root)
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
return filter(results, 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#CompleteForWorkingDir(A, L, P, ...) abort
let path = a:0 ? a:1 : getcwd()
return fugitive#Complete(a:A, a:L, a:P, FugitiveExtractGitDir(path), path)
endfunction
2018-08-25 12:13:42 -04:00
" Section: :Gcd, :Glcd
2018-06-14 06:31:12 -04:00
2019-11-16 10:28:42 -05:00
function! fugitive#CdComplete(A, L, P) abort
2019-05-17 10:09:13 -04:00
return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
2018-06-14 06:31:12 -04:00
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#Cd(path, ...) abort
2018-08-25 12:13:42 -04:00
let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
2019-11-16 10:28:42 -05:00
if path !~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
let dir = s:Dir()
exe s:DirCheck(dir)
let path = (empty(s:Tree(dir)) ? dir : s:Tree(dir)) . '/' . path
2018-08-25 12:13:42 -04:00
endif
2022-08-08 09:45:56 -04:00
return (a:0 && a:1 ? 'lcd ' : 'cd ') . fnameescape(s:VimSlash(path))
2018-08-25 12:13:42 -04:00
endfunction
" Section: :Gstatus
2018-06-14 06:31:12 -04:00
2019-08-22 11:36:17 -04:00
function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
2021-10-11 05:30:43 -04:00
let dir = a:0 ? s:Dir(a:1) : s:Dir()
2019-08-22 11:36:17 -04:00
exe s:DirCheck(dir)
2018-06-14 06:31:12 -04:00
try
2021-10-11 05:30:43 -04:00
let mods = s:Mods(a:mods, 'Edge')
2019-08-22 11:36:17 -04:00
let file = fugitive#Find(':', dir)
2021-05-31 16:30:26 -04:00
let arg = ' +setl\ foldmarker=<<<<<<<<,>>>>>>>>\|let\ w:fugitive_status=FugitiveGitDir() ' .
2019-03-08 06:04:56 -05:00
\ s:fnameescape(file)
2021-05-05 04:25:00 -04:00
for tabnr in [tabpagenr()] + (mods =~# '\<tab\>' ? range(1, tabpagenr('$')) : [])
let bufs = tabpagebuflist(tabnr)
for winnr in range(1, tabpagewinnr(tabnr, '$'))
if s:cpath(file, fnamemodify(bufname(bufs[winnr-1]), ':p'))
if tabnr == tabpagenr() && winnr == winnr()
call s:ReloadStatus()
else
call s:ExpireStatus(dir)
exe tabnr . 'tabnext'
exe winnr . 'wincmd w'
endif
let w:fugitive_status = dir
1
return ''
2019-08-22 11:36:17 -04:00
endif
2021-05-05 04:25:00 -04:00
endfor
2019-01-08 05:11:54 -05:00
endfor
if a:count ==# 0
2019-03-08 06:04:56 -05:00
return mods . 'edit' . (a:bang ? '!' : '') . arg
2019-01-08 05:11:54 -05:00
elseif a:bang
2019-03-08 06:04:56 -05:00
return mods . 'pedit' . arg . '|wincmd P'
2019-01-08 05:11:54 -05:00
else
2020-04-25 21:56:16 -04:00
return mods . 'keepalt split' . arg
2019-01-08 05:11:54 -05:00
endif
2018-06-14 06:31:12 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-06-14 06:31:12 -04:00
endtry
return ''
endfunction
2019-08-22 11:36:17 -04:00
function! s:StageJump(offset, section, ...) abort
let line = search('^\%(' . a:section . '\)', 'nw')
if !line && a:0
let line = search('^\%(' . a:1 . '\)', 'nw')
endif
if line
exe line
if a:offset
for i in range(a:offset)
call search(s:file_commit_pattern . '\|^$', 'W')
if empty(getline('.')) && a:0 && getline(line('.') + 1) =~# '^\%(' . a:1 . '\)'
call search(s:file_commit_pattern . '\|^$', 'W')
endif
if empty(getline('.'))
return ''
endif
endfor
call s:StageReveal()
else
call s:StageReveal()
+
endif
endif
return ''
endfunction
2019-01-08 05:11:54 -05:00
function! s:StageSeek(info, fallback) abort
let info = a:info
2021-05-05 04:25:00 -04:00
if empty(info.heading)
2019-01-08 05:11:54 -05:00
return a:fallback
endif
2021-10-11 05:30:43 -04:00
let line = search('^' . escape(info.heading, '^$.*[]~\') . ' (\d\++\=)$', 'wn')
2019-01-08 05:11:54 -05:00
if !line
2019-08-22 11:36:17 -04:00
for section in get({'Staged': ['Unstaged', 'Untracked'], 'Unstaged': ['Untracked', 'Staged'], 'Untracked': ['Unstaged', 'Staged']}, info.section, [])
2019-01-08 05:11:54 -05:00
let line = search('^' . section, 'wn')
if line
return line + (info.index > 0 ? 1 : 0)
endif
endfor
return 1
endif
let i = 0
while len(getline(line))
let filename = matchstr(getline(line), '^[A-Z?] \zs.*')
if len(filename) &&
\ ((info.filename[-1:-1] ==# '/' && filename[0 : len(info.filename) - 1] ==# info.filename) ||
\ (filename[-1:-1] ==# '/' && filename ==# info.filename[0 : len(filename) - 1]) ||
\ filename ==# info.filename)
if info.offset < 0
return line
else
if getline(line+1) !~# '^@'
exe s:StageInline('show', line)
endif
if getline(line+1) !~# '^@'
return line
endif
let type = info.sigil ==# '-' ? '-' : '+'
let offset = -1
while offset < info.offset
let line += 1
if getline(line) =~# '^@'
let offset = +matchstr(getline(line), type . '\zs\d\+') - 1
elseif getline(line) =~# '^[ ' . type . ']'
let offset += 1
elseif getline(line) !~# '^[ @\+-]'
return line - 1
endif
endwhile
return line
endif
endif
let commit = matchstr(getline(line), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\+')
if len(commit) && commit ==# info.commit
return line
endif
if i ==# info.index
let backup = line
endif
let i += getline(line) !~# '^[ @\+-]'
let line += 1
endwhile
return exists('backup') ? backup : line - 1
endfunction
2019-11-16 10:28:42 -05:00
function! s:DoAutocmdChanged(dir) abort
let dir = a:dir is# -2 ? '' : FugitiveGitDir(a:dir)
if empty(dir) || !exists('#User#FugitiveChanged') || exists('g:fugitive_event')
return ''
endif
try
let g:fugitive_event = dir
2021-10-11 05:30:43 -04:00
if type(a:dir) == type({}) && has_key(a:dir, 'args') && has_key(a:dir, 'exit_status')
2021-05-05 04:25:00 -04:00
let g:fugitive_result = a:dir
endif
2019-11-16 10:28:42 -05:00
exe s:DoAutocmd('User FugitiveChanged')
finally
2021-05-05 04:25:00 -04:00
unlet! g:fugitive_event g:fugitive_result
2019-11-16 10:28:42 -05:00
" Force statusline reload with the buffer's Git dir
2021-10-28 15:48:21 -04:00
let &l:ro = &l:ro
2019-11-16 10:28:42 -05:00
endtry
return ''
endfunction
function! s:ReloadStatusBuffer(...) abort
2019-03-08 06:04:56 -05:00
if get(b:, 'fugitive_type', '') !=# 'index'
return ''
endif
2019-01-08 05:11:54 -05:00
let original_lnum = a:0 ? a:1 : line('.')
let info = s:StageInfo(original_lnum)
2021-10-28 15:48:21 -04:00
call fugitive#BufReadStatus(0)
2021-05-05 04:25:00 -04:00
call setpos('.', [0, s:StageSeek(info, original_lnum), 1, 0])
2019-01-08 05:11:54 -05:00
return ''
endfunction
2019-11-16 10:28:42 -05:00
function! s:ReloadStatus(...) abort
call s:ExpireStatus(-1)
call s:ReloadStatusBuffer(a:0 ? a:1 : line('.'))
exe s:DoAutocmdChanged(-1)
return ''
endfunction
2019-08-22 11:36:17 -04:00
let s:last_time = reltime()
if !exists('s:last_times')
let s:last_times = {}
endif
function! s:ExpireStatus(bufnr) abort
2021-10-11 05:30:43 -04:00
if a:bufnr is# -2 || a:bufnr is# 0
2019-11-16 10:28:42 -05:00
let s:head_cache = {}
2019-08-22 11:36:17 -04:00
let s:last_time = reltime()
return ''
endif
2022-08-08 09:45:56 -04:00
let head_file = fugitive#Find('.git/HEAD', a:bufnr)
if !empty(head_file)
let s:last_times[s:Tree(a:bufnr) . '/'] = reltime()
if has_key(s:head_cache, head_file)
call remove(s:head_cache, head_file)
2019-11-16 10:28:42 -05:00
endif
2019-08-22 11:36:17 -04:00
endif
return ''
endfunction
function! s:ReloadWinStatus(...) abort
if get(b:, 'fugitive_type', '') !=# 'index' || &modified
2018-06-14 06:31:12 -04:00
return
endif
2019-08-22 11:36:17 -04:00
if !exists('b:fugitive_reltime')
2019-11-16 10:28:42 -05:00
exe s:ReloadStatusBuffer()
2019-08-22 11:36:17 -04:00
return
endif
let t = b:fugitive_reltime
if reltimestr(reltime(s:last_time, t)) =~# '-\|\d\{10\}\.' ||
2022-08-08 09:45:56 -04:00
\ reltimestr(reltime(get(s:last_times, s:Tree() . '/', t), t)) =~# '-\|\d\{10\}\.'
2019-11-16 10:28:42 -05:00
exe s:ReloadStatusBuffer()
2019-08-22 11:36:17 -04:00
endif
endfunction
function! s:ReloadTabStatus(...) abort
let mytab = tabpagenr()
let tab = a:0 ? a:1 : mytab
2021-05-05 04:25:00 -04:00
let winnr = 1
while winnr <= tabpagewinnr(tab, '$')
2019-08-22 11:36:17 -04:00
if getbufvar(tabpagebuflist(tab)[winnr-1], 'fugitive_type') ==# 'index'
execute 'tabnext '.tab
if winnr != winnr()
execute winnr.'wincmd w'
let restorewinnr = 1
endif
try
call s:ReloadWinStatus()
finally
if exists('restorewinnr')
unlet restorewinnr
wincmd p
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
execute 'tabnext '.mytab
endtry
endif
2021-05-05 04:25:00 -04:00
let winnr += 1
endwhile
2019-08-22 11:36:17 -04:00
unlet! t:fugitive_reload_status
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#DidChange(...) abort
2019-11-16 10:28:42 -05:00
call s:ExpireStatus(a:0 ? a:1 : -1)
2021-10-11 05:30:43 -04:00
if a:0 > 1 ? a:2 : (!a:0 || a:1 isnot# 0)
2019-08-22 11:36:17 -04:00
let t = reltime()
let t:fugitive_reload_status = t
2022-08-08 09:45:56 -04:00
for tabnr in range(1, tabpagenr('$'))
2019-08-22 11:36:17 -04:00
call settabvar(tabnr, 'fugitive_reload_status', t)
2018-06-14 06:31:12 -04:00
endfor
2019-08-22 11:36:17 -04:00
call s:ReloadTabStatus()
else
call s:ReloadWinStatus()
2021-05-05 04:25:00 -04:00
return ''
2019-08-22 11:36:17 -04:00
endif
2021-05-05 04:25:00 -04:00
exe s:DoAutocmdChanged(a:0 ? a:1 : -1)
2019-11-16 10:28:42 -05:00
return ''
2018-06-14 06:31:12 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#ReloadStatus(...) abort
return call('fugitive#DidChange', a:000)
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#EfmDir(...) abort
let dir = matchstr(a:0 ? a:1 : &errorformat, '\c,%\\&\%(git\|fugitive\)_\=dir=\zs\%(\\.\|[^,]\)*')
let dir = substitute(dir, '%%', '%', 'g')
let dir = substitute(dir, '\\\ze[\,]', '', 'g')
return dir
2019-03-08 06:04:56 -05:00
endfunction
augroup fugitive_status
autocmd!
2021-10-11 05:30:43 -04:00
autocmd BufWritePost * call fugitive#DidChange(+expand('<abuf>'), 0)
autocmd User FileChmodPost,FileUnlinkPost call fugitive#DidChange(+expand('<abuf>'), 0)
autocmd ShellCmdPost,ShellFilterPost * nested call fugitive#DidChange(0)
2019-11-16 10:28:42 -05:00
autocmd BufDelete * nested
\ if getbufvar(+expand('<abuf>'), 'buftype') ==# 'terminal' |
\ if !empty(FugitiveGitDir(+expand('<abuf>'))) |
2021-10-11 05:30:43 -04:00
\ call fugitive#DidChange(+expand('<abuf>')) |
2019-11-16 10:28:42 -05:00
\ else |
2021-10-11 05:30:43 -04:00
\ call fugitive#DidChange(0) |
2019-11-16 10:28:42 -05:00
\ endif |
\ endif
autocmd QuickFixCmdPost make,lmake,[cl]file,[cl]getfile nested
2021-10-11 05:30:43 -04:00
\ call fugitive#DidChange(fugitive#EfmDir())
autocmd FocusGained *
\ if get(g:, 'fugitive_focus_gained', !has('win32')) |
\ call fugitive#DidChange(0) |
\ endif
2022-08-08 09:45:56 -04:00
autocmd BufEnter index,index.lock,fugitive://*//
2019-08-22 11:36:17 -04:00
\ call s:ReloadWinStatus()
autocmd TabEnter *
\ if exists('t:fugitive_reload_status') |
\ call s:ReloadTabStatus() |
\ endif
2019-03-08 06:04:56 -05:00
augroup END
2019-01-08 05:11:54 -05:00
function! s:StageInfo(...) abort
let lnum = a:0 ? a:1 : line('.')
2019-03-08 06:04:56 -05:00
let sigil = matchstr(getline(lnum), '^[ @\+-]')
2019-01-08 05:11:54 -05:00
let offset = -1
2019-03-08 06:04:56 -05:00
if len(sigil)
2022-08-08 09:45:56 -04:00
let [lnum, old_lnum, new_lnum] = s:HunkPosition(lnum)
let offset = sigil ==# '-' ? old_lnum : new_lnum
2019-01-08 05:11:54 -05:00
while getline(lnum) =~# '^[ @\+-]'
let lnum -= 1
endwhile
2018-06-14 06:31:12 -04:00
endif
2019-01-08 05:11:54 -05:00
let slnum = lnum + 1
2021-07-04 16:47:44 -04:00
let heading = ''
2019-01-08 05:11:54 -05:00
let index = 0
2021-07-04 16:47:44 -04:00
while len(getline(slnum - 1)) && empty(heading)
2019-01-08 05:11:54 -05:00
let slnum -= 1
2021-10-11 05:30:43 -04:00
let heading = matchstr(getline(slnum), '^\u\l\+.\{-\}\ze (\d\++\=)$')
2021-07-04 16:47:44 -04:00
if empty(heading) && getline(slnum) !~# '^[ @\+-]'
2019-01-08 05:11:54 -05:00
let index += 1
endif
2018-06-14 06:31:12 -04:00
endwhile
2019-08-22 11:36:17 -04:00
let text = matchstr(getline(lnum), '^[A-Z?] \zs.*')
2021-07-30 16:52:54 -04:00
let file = get(get(b:fugitive_files, heading, {}), text, {})
let relative = get(file, 'relative', len(text) ? [text] : [])
2021-07-04 16:47:44 -04:00
return {'section': matchstr(heading, '^\u\l\+'),
\ 'heading': heading,
2019-01-08 05:11:54 -05:00
\ 'sigil': sigil,
\ 'offset': offset,
2019-08-22 11:36:17 -04:00
\ 'filename': text,
2021-07-30 16:52:54 -04:00
\ 'relative': copy(relative),
\ 'paths': map(copy(relative), 's:Tree() . "/" . v:val'),
2019-01-08 05:11:54 -05:00
\ 'commit': matchstr(getline(lnum), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze '),
\ 'status': matchstr(getline(lnum), '^[A-Z?]\ze \|^\%(\x\x\x\)\@!\l\+\ze [0-9a-f]'),
2021-07-30 16:52:54 -04:00
\ 'submodule': get(file, 'submodule', ''),
2019-01-08 05:11:54 -05:00
\ 'index': index}
endfunction
2019-03-08 06:04:56 -05:00
function! s:Selection(arg1, ...) abort
if a:arg1 ==# 'n'
let arg1 = line('.')
2019-08-22 11:36:17 -04:00
let arg2 = -v:count
2019-03-08 06:04:56 -05:00
elseif a:arg1 ==# 'v'
let arg1 = line("'<")
let arg2 = line("'>")
else
let arg1 = a:arg1
let arg2 = a:0 ? a:1 : 0
endif
let first = arg1
if arg2 < 0
2019-11-16 10:28:42 -05:00
let last = first - arg2 - 1
2019-03-08 06:04:56 -05:00
elseif arg2 > 0
let last = arg2
else
let last = first
endif
2022-08-08 09:45:56 -04:00
while first <= line('$') && getline(first) =~# '^$\|^[A-Z][a-z]'
2019-03-08 06:04:56 -05:00
let first += 1
endwhile
if first > last || &filetype !=# 'fugitive'
return []
endif
let flnum = first
while getline(flnum) =~# '^[ @\+-]'
let flnum -= 1
endwhile
let slnum = flnum + 1
2021-07-04 16:47:44 -04:00
let heading = ''
2019-03-08 06:04:56 -05:00
let index = 0
2021-07-04 16:47:44 -04:00
while empty(heading)
2019-03-08 06:04:56 -05:00
let slnum -= 1
2021-10-11 05:30:43 -04:00
let heading = matchstr(getline(slnum), '^\u\l\+.\{-\}\ze (\d\++\=)$')
2019-03-08 06:04:56 -05:00
if empty(heading) && getline(slnum) !~# '^[ @\+-]'
let index += 1
endif
endwhile
let results = []
let template = {
\ 'heading': heading,
2021-07-04 16:47:44 -04:00
\ 'section': matchstr(heading, '^\u\l\+'),
2019-03-08 06:04:56 -05:00
\ 'filename': '',
2019-08-22 11:36:17 -04:00
\ 'relative': [],
2019-03-08 06:04:56 -05:00
\ 'paths': [],
\ 'commit': '',
\ 'status': '',
\ 'patch': 0,
\ 'index': index}
let line = getline(flnum)
let lnum = first - (arg1 == flnum ? 0 : 1)
let root = s:Tree() . '/'
while lnum <= last
2021-10-11 05:30:43 -04:00
let heading = matchstr(line, '^\u\l\+\ze.\{-\}\ze (\d\++\=)$')
2021-07-04 16:47:44 -04:00
if len(heading)
let template.heading = heading
let template.section = matchstr(heading, '^\u\l\+')
2019-03-08 06:04:56 -05:00
let template.index = 0
elseif line =~# '^[ @\+-]'
let template.index -= 1
if !results[-1].patch
let results[-1].patch = lnum
endif
let results[-1].lnum = lnum
elseif line =~# '^[A-Z?] '
2021-07-30 16:52:54 -04:00
let text = matchstr(line, '^[A-Z?] \zs.*')
let file = get(get(b:fugitive_files, template.heading, {}), text, {})
let relative = get(file, 'relative', len(text) ? [text] : [])
2019-03-08 06:04:56 -05:00
call add(results, extend(deepcopy(template), {
\ 'lnum': lnum,
2021-07-30 16:52:54 -04:00
\ 'filename': text,
\ 'relative': copy(relative),
\ 'paths': map(copy(relative), 'root . v:val'),
2019-03-08 06:04:56 -05:00
\ 'status': matchstr(line, '^[A-Z?]'),
\ }))
elseif line =~# '^\x\x\x\+ '
call add(results, extend({
\ 'lnum': lnum,
\ 'commit': matchstr(line, '^\x\x\x\+'),
\ }, template, 'keep'))
elseif line =~# '^\l\+ \x\x\x\+ '
call add(results, extend({
\ 'lnum': lnum,
\ 'commit': matchstr(line, '^\l\+ \zs\x\x\x\+'),
\ 'status': matchstr(line, '^\l\+'),
\ }, template, 'keep'))
endif
let lnum += 1
let template.index += 1
let line = getline(lnum)
endwhile
if len(results) && results[0].patch && arg2 == 0
while getline(results[0].patch) =~# '^[ \+-]'
let results[0].patch -= 1
endwhile
while getline(results[0].lnum + 1) =~# '^[ \+-]'
let results[0].lnum += 1
endwhile
endif
return results
endfunction
function! s:StageArgs(visual) abort
let commits = []
let paths = []
for record in s:Selection(a:visual ? 'v' : 'n')
if len(record.commit)
call add(commits, record.commit)
endif
call extend(paths, record.paths)
endfor
if s:cpath(s:Tree(), getcwd())
call map(paths, 'fugitive#Path(v:val, "./")')
endif
return join(map(commits + paths, 's:fnameescape(v:val)'), ' ')
endfunction
function! s:Do(action, visual) abort
let line = getline('.')
2019-08-22 11:36:17 -04:00
let reload = 0
2019-11-16 10:28:42 -05:00
if !a:visual && !v:count && line =~# '^[A-Z][a-z]'
2019-03-08 06:04:56 -05:00
let header = matchstr(line, '^\S\+\ze:')
if len(header) && exists('*s:Do' . a:action . header . 'Header')
2019-08-22 11:36:17 -04:00
let reload = s:Do{a:action}{header}Header(matchstr(line, ': \zs.*')) > 0
else
let section = matchstr(line, '^\S\+')
if exists('*s:Do' . a:action . section . 'Heading')
let reload = s:Do{a:action}{section}Heading(line) > 0
endif
2019-03-08 06:04:56 -05:00
endif
2019-08-22 11:36:17 -04:00
return reload ? s:ReloadStatus() : ''
2019-03-08 06:04:56 -05:00
endif
let selection = s:Selection(a:visual ? 'v' : 'n')
if empty(selection)
return ''
endif
call filter(selection, 'v:val.section ==# selection[0].section')
let status = 0
let err = ''
try
for record in selection
if exists('*s:Do' . a:action . record.section)
let status = s:Do{a:action}{record.section}(record)
else
continue
endif
if !status
return ''
endif
let reload = reload || (status > 0)
endfor
if status < 0
execute record.lnum + 1
endif
2019-08-22 11:36:17 -04:00
let success = 1
2019-03-08 06:04:56 -05:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2019-03-08 06:04:56 -05:00
finally
if reload
execute s:ReloadStatus()
endif
2019-08-22 11:36:17 -04:00
if exists('success')
call s:StageReveal()
endif
2019-03-08 06:04:56 -05:00
endtry
return ''
endfunction
2019-11-16 10:28:42 -05:00
function! s:StageReveal() abort
exe 'normal! zv'
let begin = line('.')
2019-01-08 05:11:54 -05:00
if getline(begin) =~# '^@'
2019-03-08 06:04:56 -05:00
let end = begin + 1
2019-01-08 05:11:54 -05:00
while getline(end) =~# '^[ \+-]'
let end += 1
endwhile
2019-08-22 11:36:17 -04:00
elseif getline(begin) =~# '^commit '
let end = begin
while end < line('$') && getline(end + 1) !~# '^commit '
let end += 1
endwhile
elseif getline(begin) =~# s:section_pattern
let end = begin
while len(getline(end + 1))
let end += 1
endwhile
endif
if exists('end')
while line('.') > line('w0') + &scrolloff && end > line('w$')
2019-01-08 05:11:54 -05:00
execute "normal! \<C-E>"
endwhile
2018-06-14 06:31:12 -04:00
endif
endfunction
2019-08-22 11:36:17 -04:00
let s:file_pattern = '^[A-Z?] .\|^diff --'
let s:file_commit_pattern = s:file_pattern . '\|^\%(\l\{3,\} \)\=[0-9a-f]\{4,\} '
let s:item_pattern = s:file_commit_pattern . '\|^@@'
function! s:NextHunk(count) abort
if &filetype ==# 'fugitive' && getline('.') =~# s:file_pattern
exe s:StageInline('show')
endif
for i in range(a:count)
if &filetype ==# 'fugitive'
call search(s:file_pattern . '\|^@', 'W')
if getline('.') =~# s:file_pattern
exe s:StageInline('show')
if getline(line('.') + 1) =~# '^@'
+
endif
endif
else
call search('^@@', 'W')
endif
endfor
call s:StageReveal()
return '.'
endfunction
function! s:PreviousHunk(count) abort
2022-08-08 09:45:56 -04:00
normal! 0
2019-08-22 11:36:17 -04:00
for i in range(a:count)
if &filetype ==# 'fugitive'
2022-08-08 09:45:56 -04:00
if getline('.') =~# '^@' && getline(line('.') - 1) =~# s:file_pattern
-
endif
2019-08-22 11:36:17 -04:00
let lnum = search(s:file_pattern . '\|^@','Wbn')
call s:StageInline('show', lnum)
call search('^? .\|^@','Wb')
else
call search('^@@', 'Wb')
endif
endfor
call s:StageReveal()
return '.'
endfunction
function! s:NextFile(count) abort
for i in range(a:count)
exe s:StageInline('hide')
if !search(s:file_pattern, 'W')
break
endif
endfor
exe s:StageInline('hide')
return '.'
endfunction
function! s:PreviousFile(count) abort
exe s:StageInline('hide')
2022-08-08 09:45:56 -04:00
normal! 0
2019-08-22 11:36:17 -04:00
for i in range(a:count)
if !search(s:file_pattern, 'Wb')
break
endif
exe s:StageInline('hide')
endfor
return '.'
endfunction
function! s:NextItem(count) abort
for i in range(a:count)
if !search(s:item_pattern, 'W') && getline('.') !~# s:item_pattern
call search('^commit ', 'W')
endif
endfor
call s:StageReveal()
return '.'
endfunction
function! s:PreviousItem(count) abort
2022-08-08 09:45:56 -04:00
normal! 0
2018-06-14 06:31:12 -04:00
for i in range(a:count)
2021-10-28 15:48:21 -04:00
if !search(s:item_pattern, 'Wb') && getline('.') !~# s:item_pattern
call search('^commit ', 'Wb')
2019-08-22 11:36:17 -04:00
endif
2018-06-14 06:31:12 -04:00
endfor
2019-01-08 05:11:54 -05:00
call s:StageReveal()
2018-06-14 06:31:12 -04:00
return '.'
endfunction
2019-08-22 11:36:17 -04:00
let s:section_pattern = '^[A-Z][a-z][^:]*$'
let s:section_commit_pattern = s:section_pattern . '\|^commit '
function! s:NextSection(count) abort
let orig = line('.')
if getline('.') !~# '^commit '
-
endif
for i in range(a:count)
if !search(s:section_commit_pattern, 'W')
break
endif
endfor
if getline('.') =~# s:section_commit_pattern
call s:StageReveal()
return getline('.') =~# s:section_pattern ? '+' : ':'
2018-06-14 06:31:12 -04:00
else
2019-08-22 11:36:17 -04:00
return orig
endif
endfunction
function! s:PreviousSection(count) abort
let orig = line('.')
if getline('.') !~# '^commit '
-
endif
2022-08-08 09:45:56 -04:00
normal! 0
2019-08-22 11:36:17 -04:00
for i in range(a:count)
if !search(s:section_commit_pattern . '\|\%^', 'bW')
break
endif
endfor
if getline('.') =~# s:section_commit_pattern || line('.') == 1
2019-01-08 05:11:54 -05:00
call s:StageReveal()
2019-08-22 11:36:17 -04:00
return getline('.') =~# s:section_pattern ? '+' : ':'
else
return orig
endif
endfunction
function! s:NextSectionEnd(count) abort
+
if empty(getline('.'))
+
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
for i in range(a:count)
if !search(s:section_commit_pattern, 'W')
return '$'
endif
endfor
return search('^.', 'Wb')
endfunction
function! s:PreviousSectionEnd(count) abort
let old = line('.')
for i in range(a:count)
if search(s:section_commit_pattern, 'Wb') <= 1
exe old
if i
break
else
return ''
endif
endif
let old = line('.')
endfor
return search('^.', 'Wb')
endfunction
function! s:PatchSearchExpr(reverse) abort
let line = getline('.')
if col('.') ==# 1 && line =~# '^[+-]'
if line =~# '^[+-]\{3\} '
let pattern = '^[+-]\{3\} ' . substitute(escape(strpart(line, 4), '^$.*[]~\'), '^\w/', '\\w/', '') . '$'
else
let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
endif
if a:reverse
2019-12-01 14:18:45 -05:00
return '?' . escape(pattern, '/?') . "\<CR>"
2019-08-22 11:36:17 -04:00
else
2019-12-01 14:18:45 -05:00
return '/' . escape(pattern, '/') . "\<CR>"
2019-08-22 11:36:17 -04:00
endif
endif
return a:reverse ? '#' : '*'
2018-06-14 06:31:12 -04:00
endfunction
2019-01-08 05:11:54 -05:00
function! s:StageInline(mode, ...) abort
2019-08-22 11:36:17 -04:00
if &filetype !=# 'fugitive'
return ''
endif
2019-01-08 05:11:54 -05:00
let lnum1 = a:0 ? a:1 : line('.')
let lnum = lnum1 + 1
2021-05-05 04:25:00 -04:00
if a:0 > 1 && a:2 == 0 && lnum1 == 1
let lnum = line('$') - 1
elseif a:0 > 1 && a:2 == 0
2019-01-08 05:11:54 -05:00
let info = s:StageInfo(lnum - 1)
2019-08-22 11:36:17 -04:00
if empty(info.paths) && len(info.section)
2019-01-08 05:11:54 -05:00
while len(getline(lnum))
let lnum += 1
endwhile
2018-06-14 06:31:12 -04:00
endif
2019-01-08 05:11:54 -05:00
elseif a:0 > 1
let lnum += a:2 - 1
2018-06-14 06:31:12 -04:00
endif
2019-01-08 05:11:54 -05:00
while lnum > lnum1
let lnum -= 1
while lnum > 0 && getline(lnum) =~# '^[ @\+-]'
let lnum -= 1
endwhile
let info = s:StageInfo(lnum)
if !has_key(b:fugitive_diff, info.section)
continue
endif
if getline(lnum + 1) =~# '^[ @\+-]'
let lnum2 = lnum + 1
while getline(lnum2 + 1) =~# '^[ @\+-]'
let lnum2 += 1
endwhile
if a:mode !=# 'show'
setlocal modifiable noreadonly
exe 'silent keepjumps ' . (lnum + 1) . ',' . lnum2 . 'delete _'
call remove(b:fugitive_expanded[info.section], info.filename)
setlocal nomodifiable readonly nomodified
endif
continue
endif
2019-08-22 11:36:17 -04:00
if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADMRU]$' || a:mode ==# 'hide'
2019-01-08 05:11:54 -05:00
continue
endif
let mode = ''
let diff = []
2022-08-08 09:45:56 -04:00
if info.status ==# 'U'
let diff_header = 'diff --cc ' . s:Quote(info.relative[0])
else
let diff_header = 'diff --git ' . s:Quote(info.relative[-1]) . ' ' . s:Quote(info.relative[0])
endif
let stdout = fugitive#Wait(b:fugitive_diff[info.section]).stdout
let start = index(stdout, diff_header)
if start == -1
continue
endif
let index = start + 1
while get(stdout, index, '@@') !~# '^@@\|^diff '
2019-01-08 05:11:54 -05:00
let index += 1
2022-08-08 09:45:56 -04:00
endwhile
while get(stdout, index, '') =~# '^[@ \+-]'
call add(diff, stdout[index])
let index += 1
endwhile
2019-01-08 05:11:54 -05:00
if len(diff)
setlocal modifiable noreadonly
silent call append(lnum, diff)
2022-08-08 09:45:56 -04:00
let b:fugitive_expanded[info.section][info.filename] = [start]
2019-01-08 05:11:54 -05:00
setlocal nomodifiable readonly nomodified
2021-05-05 04:25:00 -04:00
if foldclosed(lnum+1) > 0
silent exe (lnum+1) . ',' . (lnum+len(diff)) . 'foldopen!'
endif
2019-01-08 05:11:54 -05:00
endif
endwhile
return lnum
2018-06-14 06:31:12 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:NextExpandedHunk(count) abort
2019-03-08 06:04:56 -05:00
for i in range(a:count)
2019-08-22 11:36:17 -04:00
call s:StageInline('show', line('.'), 1)
call search(s:file_pattern . '\|^@','W')
2019-03-08 06:04:56 -05:00
endfor
return '.'
endfunction
2018-06-14 06:31:12 -04:00
function! s:StageDiff(diff) abort
2019-01-08 05:11:54 -05:00
let lnum = line('.')
let info = s:StageInfo(lnum)
let prefix = info.offset > 0 ? '+' . info.offset : ''
2021-05-05 04:25:00 -04:00
if info.submodule =~# '^S'
2019-11-16 10:28:42 -05:00
if info.section ==# 'Staged'
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff --submodule=log --cached -- ' . info.paths[0]
2021-05-05 04:25:00 -04:00
elseif info.submodule =~# '^SC'
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff --submodule=log -- ' . info.paths[0]
2019-11-16 10:28:42 -05:00
else
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff --submodule=diff -- ' . info.paths[0]
2019-11-16 10:28:42 -05:00
endif
elseif empty(info.paths) && info.section ==# 'Staged'
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff --cached'
2019-08-22 11:36:17 -04:00
elseif empty(info.paths)
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff'
2019-08-22 11:36:17 -04:00
elseif len(info.paths) > 1
execute 'Gedit' . prefix s:fnameescape(':0:' . info.paths[0])
2021-05-05 04:25:00 -04:00
return a:diff . '! @:'.s:fnameescape(info.paths[1])
2019-01-08 05:11:54 -05:00
elseif info.section ==# 'Staged' && info.sigil ==# '-'
2019-08-22 11:36:17 -04:00
execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
return a:diff . '! :0:%'
2019-01-08 05:11:54 -05:00
elseif info.section ==# 'Staged'
2019-08-22 11:36:17 -04:00
execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
return a:diff . '! @:%'
2019-01-08 05:11:54 -05:00
elseif info.sigil ==# '-'
2019-08-22 11:36:17 -04:00
execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
return a:diff . '! :(top)%'
2018-06-14 06:31:12 -04:00
else
2019-08-22 11:36:17 -04:00
execute 'Gedit' prefix s:fnameescape(':(top)'.info.paths[0])
return a:diff . '!'
2018-06-14 06:31:12 -04:00
endif
endfunction
function! s:StageDiffEdit() abort
2019-01-08 05:11:54 -05:00
let info = s:StageInfo(line('.'))
2019-08-22 11:36:17 -04:00
let arg = (empty(info.paths) ? s:Tree() : info.paths[0])
2019-01-08 05:11:54 -05:00
if info.section ==# 'Staged'
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff --cached '.s:fnameescape(arg)
2019-01-08 05:11:54 -05:00
elseif info.status ==# '?'
2019-08-22 11:36:17 -04:00
call s:TreeChomp('add', '--intent-to-add', '--', arg)
2019-03-08 06:04:56 -05:00
return s:ReloadStatus()
2018-06-14 06:31:12 -04:00
else
2020-04-25 21:56:16 -04:00
return 'Git --paginate diff --no-ext-diff '.s:fnameescape(arg)
2018-06-14 06:31:12 -04:00
endif
endfunction
2019-03-08 06:04:56 -05:00
function! s:StageApply(info, reverse, extra) abort
2019-08-22 11:36:17 -04:00
if a:info.status ==# 'R'
2021-05-05 04:25:00 -04:00
throw 'fugitive: patching renamed file not yet supported'
2019-08-22 11:36:17 -04:00
endif
2019-03-08 06:04:56 -05:00
let cmd = ['apply', '-p0', '--recount'] + a:extra
2019-01-08 05:11:54 -05:00
let info = a:info
2019-03-08 06:04:56 -05:00
let start = info.patch
let end = info.lnum
2019-01-08 05:11:54 -05:00
let lines = getline(start, end)
if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
2019-03-08 06:04:56 -05:00
return -1
2019-01-08 05:11:54 -05:00
endif
2021-05-05 04:25:00 -04:00
while getline(end) =~# '^[-+\ ]'
2019-01-08 05:11:54 -05:00
let end += 1
2021-05-05 04:25:00 -04:00
if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . '\ ]'
2019-01-08 05:11:54 -05:00
call add(lines, ' ' . getline(end)[1:-1])
endif
endwhile
while start > 0 && getline(start) !~# '^@'
let start -= 1
if getline(start) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
call insert(lines, ' ' . getline(start)[1:-1])
elseif getline(start) =~# '^@'
call insert(lines, getline(start))
endif
endwhile
2019-08-22 11:36:17 -04:00
if start == 0
2021-06-23 05:57:12 -04:00
throw 'fugitive: could not find hunk'
2019-08-22 11:36:17 -04:00
elseif getline(start) !~# '^@@ '
throw 'fugitive: cannot apply conflict hunk'
2019-01-08 05:11:54 -05:00
endif
let i = b:fugitive_expanded[info.section][info.filename][0]
let head = []
2021-10-11 05:30:43 -04:00
let diff_lines = fugitive#Wait(b:fugitive_diff[info.section]).stdout
while get(diff_lines, i, '@') !~# '^@'
let line = diff_lines[i]
2020-12-04 16:15:32 -05:00
if line ==# '--- /dev/null'
2021-10-11 05:30:43 -04:00
call add(head, '--- ' . get(diff_lines, i + 1, '')[4:-1])
2020-12-04 16:15:32 -05:00
elseif line !~# '^new file '
call add(head, line)
endif
2019-01-08 05:11:54 -05:00
let i += 1
endwhile
call extend(lines, head, 'keep')
let temp = tempname()
call writefile(lines, temp)
if a:reverse
call add(cmd, '--reverse')
endif
call extend(cmd, ['--', temp])
2021-10-11 05:30:43 -04:00
let output = s:ChompStderr(cmd)
if empty(output)
2019-03-08 06:04:56 -05:00
return 1
endif
call s:throw(output)
2019-01-08 05:11:54 -05:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:StageDelete(lnum1, lnum2, count) abort
let restore = []
2021-05-05 04:25:00 -04:00
2019-08-22 11:36:17 -04:00
let err = ''
2021-05-05 04:25:00 -04:00
let did_conflict_err = 0
2021-10-11 05:30:43 -04:00
let reset_commit = matchstr(getline(a:lnum1), '^Un\w\+ \%(to\| from\) \zs\S\+')
2019-08-22 11:36:17 -04:00
try
for info in s:Selection(a:lnum1, a:lnum2)
if empty(info.paths)
2021-10-11 05:30:43 -04:00
if len(info.commit)
let reset_commit = info.commit . '^'
endif
2019-08-22 11:36:17 -04:00
continue
endif
2021-05-05 04:25:00 -04:00
let sub = get(get(get(b:fugitive_files, info.section, {}), info.filename, {}), 'submodule')
if sub =~# '^S' && info.status ==# 'M'
let undo = 'Git checkout ' . fugitive#RevParse('HEAD', FugitiveExtractGitDir(info.paths[0]))[0:10] . ' --'
elseif sub =~# '^S'
let err .= '|echoerr ' . string('fugitive: will not touch submodule ' . string(info.relative[0]))
break
elseif info.status ==# 'D'
let undo = 'GRemove'
2019-11-16 10:28:42 -05:00
elseif info.paths[0] =~# '/$'
let err .= '|echoerr ' . string('fugitive: will not delete directory ' . string(info.relative[0]))
break
else
let undo = 'Gread ' . s:TreeChomp('hash-object', '-w', '--', info.paths[0])[0:10]
endif
2019-08-22 11:36:17 -04:00
if info.patch
call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
2021-05-05 04:25:00 -04:00
elseif sub =~# '^S'
if info.section ==# 'Staged'
call s:TreeChomp('reset', '--', info.paths[0])
endif
call s:TreeChomp('submodule', 'update', '--', info.paths[0])
2019-08-22 11:36:17 -04:00
elseif info.status ==# '?'
call s:TreeChomp('clean', '-f', '--', info.paths[0])
elseif a:count == 2
2021-05-05 04:25:00 -04:00
if get(b:fugitive_files['Staged'], info.filename, {'status': ''}).status ==# 'D'
2022-08-08 09:45:56 -04:00
call delete(info.paths[0])
2021-05-05 04:25:00 -04:00
else
call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
endif
2019-08-22 11:36:17 -04:00
elseif a:count == 3
2021-05-05 04:25:00 -04:00
if get(b:fugitive_files['Unstaged'], info.filename, {'status': ''}).status ==# 'D'
2022-08-08 09:45:56 -04:00
call delete(info.paths[0])
2021-05-05 04:25:00 -04:00
else
call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
endif
2019-08-22 11:36:17 -04:00
elseif info.status =~# '[ADU]' &&
2019-11-16 10:28:42 -05:00
\ get(b:fugitive_files[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, {'status': ''}).status =~# '[AU]'
2021-05-05 04:25:00 -04:00
if get(g:, 'fugitive_conflict_x', 0)
call s:TreeChomp('checkout', info.section ==# 'Unstaged' ? '--ours' : '--theirs', '--', info.paths[0])
else
if !did_conflict_err
let err .= '|echoerr "Use 2X for --ours or 3X for --theirs"'
let did_conflict_err = 1
endif
continue
endif
2019-08-22 11:36:17 -04:00
elseif info.status ==# 'U'
2022-08-08 09:45:56 -04:00
call delete(info.paths[0])
2019-08-22 11:36:17 -04:00
elseif info.status ==# 'A'
call s:TreeChomp('rm', '-f', '--', info.paths[0])
elseif info.section ==# 'Unstaged'
call s:TreeChomp('checkout', '--', info.paths[0])
else
2021-05-05 04:25:00 -04:00
call s:TreeChomp('checkout', '@', '--', info.paths[0])
endif
if len(undo)
call add(restore, ':Gsplit ' . s:fnameescape(info.relative[0]) . '|' . undo)
2019-08-22 11:36:17 -04:00
endif
endfor
catch /^fugitive:/
2019-11-16 10:28:42 -05:00
let err .= '|echoerr ' . string(v:exception)
2019-08-22 11:36:17 -04:00
endtry
if empty(restore)
2021-10-11 05:30:43 -04:00
if len(reset_commit) && empty(err)
call feedkeys(':Git reset ' . reset_commit)
endif
2019-08-22 11:36:17 -04:00
return err[1:-1]
2019-01-08 05:11:54 -05:00
endif
2019-08-22 11:36:17 -04:00
exe s:ReloadStatus()
call s:StageReveal()
2021-10-11 05:30:43 -04:00
return 'checktime|redraw|echomsg ' . string('To restore, ' . join(restore, '|')) . err
2019-08-22 11:36:17 -04:00
endfunction
function! s:StageIgnore(lnum1, lnum2, count) abort
let paths = []
for info in s:Selection(a:lnum1, a:lnum2)
call extend(paths, info.relative)
endfor
call map(paths, '"/" . v:val')
2021-05-05 04:25:00 -04:00
if !a:0
let dir = fugitive#Find('.git/info/')
if !isdirectory(dir)
try
call mkdir(dir)
catch
endtry
endif
endif
2019-08-22 11:36:17 -04:00
exe 'Gsplit' (a:count ? '.gitignore' : '.git/info/exclude')
let last = line('$')
if last == 1 && empty(getline(1))
call setline(last, paths)
2019-01-08 05:11:54 -05:00
else
2019-08-22 11:36:17 -04:00
call append(last, paths)
exe last + 1
2019-01-08 05:11:54 -05:00
endif
2019-08-22 11:36:17 -04:00
return ''
2019-01-08 05:11:54 -05:00
endfunction
2019-03-08 06:04:56 -05:00
function! s:DoToggleHeadHeader(value) abort
2022-08-08 09:45:56 -04:00
exe 'edit' fnameescape(fugitive#Find('.git/'))
2019-03-08 06:04:56 -05:00
call search('\C^index$', 'wc')
endfunction
2020-06-21 11:50:44 -04:00
function! s:DoToggleHelpHeader(value) abort
exe 'help fugitive-map'
endfunction
2020-04-25 21:56:16 -04:00
function! s:DoStagePushHeader(value) abort
let remote = matchstr(a:value, '\zs[^/]\+\ze/')
if empty(remote)
let remote = '.'
endif
let branch = matchstr(a:value, '\%([^/]\+/\)\=\zs\S\+')
call feedkeys(':Git push ' . remote . ' ' . branch)
endfunction
function! s:DoTogglePushHeader(value) abort
return s:DoStagePushHeader(a:value)
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoStageUnpushedHeading(heading) abort
2019-03-08 06:04:56 -05:00
let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
if empty(remote)
let remote = '.'
2018-06-14 06:31:12 -04:00
endif
2019-03-08 06:04:56 -05:00
let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
2022-08-08 09:45:56 -04:00
if branch ==# '*'
return
endif
2021-05-05 04:25:00 -04:00
call feedkeys(':Git push ' . remote . ' ' . '@:' . 'refs/heads/' . branch)
2019-03-08 06:04:56 -05:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoToggleUnpushedHeading(heading) abort
return s:DoStageUnpushedHeading(a:heading)
endfunction
function! s:DoStageUnpushed(record) abort
2019-03-08 06:04:56 -05:00
let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
if empty(remote)
let remote = '.'
endif
let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
2022-08-08 09:45:56 -04:00
if branch ==# '*'
return
endif
2020-01-28 21:07:36 -05:00
call feedkeys(':Git push ' . remote . ' ' . a:record.commit . ':' . 'refs/heads/' . branch)
2019-03-08 06:04:56 -05:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoToggleUnpushed(record) abort
return s:DoStageUnpushed(a:record)
endfunction
function! s:DoUnstageUnpulledHeading(heading) abort
2020-01-28 21:07:36 -05:00
call feedkeys(':Git rebase')
2019-03-08 06:04:56 -05:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoToggleUnpulledHeading(heading) abort
call s:DoUnstageUnpulledHeading(a:heading)
endfunction
function! s:DoUnstageUnpulled(record) abort
2020-01-28 21:07:36 -05:00
call feedkeys(':Git rebase ' . a:record.commit)
2019-03-08 06:04:56 -05:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoToggleUnpulled(record) abort
call s:DoUnstageUnpulled(a:record)
endfunction
function! s:DoUnstageUnpushed(record) abort
2020-04-25 21:56:16 -04:00
call feedkeys(':Git -c sequence.editor=true rebase --interactive --autosquash ' . a:record.commit . '^')
2019-08-22 11:36:17 -04:00
endfunction
2019-03-08 06:04:56 -05:00
function! s:DoToggleStagedHeading(...) abort
call s:TreeChomp('reset', '-q')
return 1
endfunction
function! s:DoUnstageStagedHeading(heading) abort
return s:DoToggleStagedHeading(a:heading)
2019-01-08 05:11:54 -05:00
endfunction
2019-03-08 06:04:56 -05:00
function! s:DoToggleUnstagedHeading(...) abort
call s:TreeChomp('add', '-u')
return 1
endfunction
function! s:DoStageUnstagedHeading(heading) abort
return s:DoToggleUnstagedHeading(a:heading)
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoToggleUntrackedHeading(...) abort
call s:TreeChomp('add', '.')
return 1
endfunction
function! s:DoStageUntrackedHeading(heading) abort
return s:DoToggleUntrackedHeading(a:heading)
endfunction
2019-03-08 06:04:56 -05:00
function! s:DoToggleStaged(record) abort
if a:record.patch
return s:StageApply(a:record, 1, ['--cached'])
2019-01-08 05:11:54 -05:00
else
2019-03-08 06:04:56 -05:00
call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
return 1
endif
endfunction
function! s:DoUnstageStaged(record) abort
return s:DoToggleStaged(a:record)
endfunction
function! s:DoToggleUnstaged(record) abort
2020-12-04 16:15:32 -05:00
if a:record.patch
2019-03-08 06:04:56 -05:00
return s:StageApply(a:record, 0, ['--cached'])
else
call s:TreeChomp(['add', '-A', '--'] + a:record.paths)
return 1
endif
endfunction
function! s:DoStageUnstaged(record) abort
return s:DoToggleUnstaged(a:record)
endfunction
function! s:DoUnstageUnstaged(record) abort
if a:record.status ==# 'A'
call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
return 1
else
return -1
2019-01-08 05:11:54 -05:00
endif
2018-06-14 06:31:12 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:DoToggleUntracked(record) abort
call s:TreeChomp(['add', '--'] + a:record.paths)
return 1
endfunction
function! s:DoStageUntracked(record) abort
return s:DoToggleUntracked(a:record)
endfunction
2018-06-14 06:31:12 -04:00
function! s:StagePatch(lnum1,lnum2) abort
let add = []
let reset = []
2019-08-22 11:36:17 -04:00
let intend = []
2018-06-14 06:31:12 -04:00
for lnum in range(a:lnum1,a:lnum2)
2019-01-08 05:11:54 -05:00
let info = s:StageInfo(lnum)
2019-08-22 11:36:17 -04:00
if empty(info.paths) && info.section ==# 'Staged'
2022-08-08 09:45:56 -04:00
execute 'tab Git reset --patch'
break
2019-08-22 11:36:17 -04:00
elseif empty(info.paths) && info.section ==# 'Unstaged'
2022-08-08 09:45:56 -04:00
execute 'tab Git add --patch'
break
2019-08-22 11:36:17 -04:00
elseif empty(info.paths) && info.section ==# 'Untracked'
2022-08-08 09:45:56 -04:00
execute 'tab Git add --interactive'
break
2019-08-22 11:36:17 -04:00
elseif empty(info.paths)
2018-06-14 06:31:12 -04:00
continue
endif
execute lnum
2019-08-22 11:36:17 -04:00
if info.section ==# 'Staged'
let reset += info.relative
elseif info.section ==# 'Untracked'
let intend += info.paths
2019-01-08 05:11:54 -05:00
elseif info.status !~# '^D'
2019-08-22 11:36:17 -04:00
let add += info.relative
2018-06-14 06:31:12 -04:00
endif
endfor
try
2019-08-22 11:36:17 -04:00
if !empty(intend)
call s:TreeChomp(['add', '--intent-to-add', '--'] + intend)
endif
2018-06-14 06:31:12 -04:00
if !empty(add)
2021-10-11 05:30:43 -04:00
execute "tab Git add --patch -- ".join(map(add,'fnameescape(v:val)'))
2018-06-14 06:31:12 -04:00
endif
if !empty(reset)
2021-10-11 05:30:43 -04:00
execute "tab Git reset --patch -- ".join(map(reset,'fnameescape(v:val)'))
2018-06-14 06:31:12 -04:00
endif
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-06-14 06:31:12 -04:00
endtry
2019-01-08 05:11:54 -05:00
return s:ReloadStatus()
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
" Section: :Git commit, :Git revert
2018-06-14 06:31:12 -04:00
2020-04-25 21:56:16 -04:00
function! s:CommitInteractive(line1, line2, range, bang, mods, options, patch) abort
2021-10-11 05:30:43 -04:00
let status = s:StatusCommand(a:line1, a:line2, a:range, get(a:options, 'curwin') && a:line2 < 0 ? 0 : a:line2, a:bang, a:mods, '', '', [], a:options)
2019-08-22 11:36:17 -04:00
let status = len(status) ? status . '|' : ''
if a:patch
return status . 'if search("^Unstaged")|exe "normal >"|exe "+"|endif'
else
return status . 'if search("^Untracked\\|^Unstaged")|exe "+"|endif'
endif
endfunction
2018-06-14 06:31:12 -04:00
2020-04-25 21:56:16 -04:00
function! s:CommitSubcommand(line1, line2, range, bang, mods, options) abort
2021-05-05 04:25:00 -04:00
let argv = copy(a:options.subcommand_args)
2020-01-28 21:07:36 -05:00
let i = 0
while get(argv, i, '--') !=# '--'
if argv[i] =~# '^-[apzsneiovq].'
call insert(argv, argv[i][0:1])
let argv[i+1] = '-' . argv[i+1][2:-1]
2018-06-14 06:31:12 -04:00
else
2020-01-28 21:07:36 -05:00
let i += 1
2018-06-14 06:31:12 -04:00
endif
2020-01-28 21:07:36 -05:00
endwhile
if s:HasOpt(argv, '-i', '--interactive')
2020-04-25 21:56:16 -04:00
return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, a:options, 0)
2020-01-28 21:07:36 -05:00
elseif s:HasOpt(argv, '-p', '--patch')
2020-04-25 21:56:16 -04:00
return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, a:options, 1)
2020-01-28 21:07:36 -05:00
else
return {}
endif
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:RevertSubcommand(line1, line2, range, bang, mods, options) abort
return {'insert_args': ['--edit']}
2019-08-22 11:36:17 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#CommitComplete(A, L, P, ...) abort
let dir = a:0 ? a:1 : s:Dir()
2018-07-30 17:18:16 -04:00
if a:A =~# '^--fixup=\|^--squash='
2020-04-25 21:56:16 -04:00
let commits = s:LinesError([dir, 'log', '--pretty=format:%s', '@{upstream}..'])[0]
2019-08-22 11:36:17 -04:00
let pre = matchstr(a:A, '^--\w*=''\=') . ':/^'
if pre =~# "'"
call map(commits, 'pre . string(tr(v:val, "|\"^$*[]", "......."))[1:-1]')
call filter(commits, 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
return commits
else
return s:FilterEscape(map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")'), a:A)
endif
2018-06-14 06:31:12 -04:00
else
2020-04-25 21:56:16 -04:00
return s:CompleteSub('commit', a:A, a:L, a:P, function('fugitive#CompletePath'), a:000)
2018-06-14 06:31:12 -04:00
endif
2018-07-30 17:18:16 -04:00
return []
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#RevertComplete(A, L, P, ...) abort
return s:CompleteSub('revert', a:A, a:L, a:P, function('s:CompleteRevision'), a:000)
2019-08-22 11:36:17 -04:00
endfunction
2020-04-25 21:56:16 -04:00
" Section: :Git merge, :Git rebase, :Git pull
2018-06-14 06:31:12 -04:00
2020-04-25 21:56:16 -04:00
function! fugitive#MergeComplete(A, L, P, ...) abort
return s:CompleteSub('merge', a:A, a:L, a:P, function('s:CompleteRevision'), a:000)
2019-08-22 11:36:17 -04:00
endfunction
2018-06-14 06:31:12 -04:00
2020-04-25 21:56:16 -04:00
function! fugitive#RebaseComplete(A, L, P, ...) abort
return s:CompleteSub('rebase', a:A, a:L, a:P, function('s:CompleteRevision'), a:000)
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#PullComplete(A, L, P, ...) abort
return s:CompleteSub('pull', a:A, a:L, a:P, function('s:CompleteRemote'), a:000)
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:MergeSubcommand(line1, line2, range, bang, mods, options) abort
2021-05-05 04:25:00 -04:00
if empty(a:options.subcommand_args) && (
2021-10-11 05:30:43 -04:00
\ filereadable(fugitive#Find('.git/MERGE_MSG', a:options)) ||
\ isdirectory(fugitive#Find('.git/rebase-apply', a:options)) ||
\ !empty(s:TreeChomp([a:options.git_dir, 'diff-files', '--diff-filter=U'])))
return 'echoerr ":Git merge for loading conflicts has been removed in favor of :Git mergetool"'
2020-01-28 21:07:36 -05:00
endif
return {}
2019-08-22 11:36:17 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:RebaseSubcommand(line1, line2, range, bang, mods, options) abort
2021-05-05 04:25:00 -04:00
let args = a:options.subcommand_args
2020-04-25 21:56:16 -04:00
if s:HasOpt(args, '--autosquash') && !s:HasOpt(args, '-i', '--interactive')
return {'env': {'GIT_SEQUENCE_EDITOR': 'true'}, 'insert_args': ['--interactive']}
2020-01-28 21:07:36 -05:00
endif
return {}
2019-12-12 17:01:41 -05:00
endfunction
2022-08-08 09:45:56 -04:00
" Section: :Git bisect
function! s:CompleteBisect(A, L, P, ...) abort
let bisect_subcmd = matchstr(a:L, '\u\w*[! ] *.\{-\}\s\@<=\zs[^-[:space:]]\S*\ze ')
if empty(bisect_subcmd)
let subcmds = ['start', 'bad', 'new', 'good', 'old', 'terms', 'skip', 'next', 'reset', 'replay', 'log', 'run']
return s:FilterEscape(subcmds, a:A)
endif
let dir = a:0 ? a:1 : s:Dir()
return fugitive#CompleteObject(a:A, dir)
endfunction
function! fugitive#BisectComplete(A, L, P, ...) abort
return s:CompleteSub('bisect', a:A, a:L, a:P, function('s:CompleteBisect'), a:000)
endfunction
2020-01-07 07:45:07 -05:00
" 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,
2022-08-08 09:45:56 -04:00
\ 'filename': diff.filename . s:VimSlash(path),
2020-01-07 07:45:07 -05:00
\ '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 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
2020-01-28 21:07:36 -05:00
elseif a:line =~# '^\* Unmerged path .'
let file = a:line[16:-1]
return s:ToolItems(a:state, file, file, [], '')
2020-01-07 07:45:07 -05:00
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
2020-04-25 21:56:16 -04:00
function! s:ToolStream(line1, line2, range, bang, mods, options, args, state) abort
2020-01-07 07:45:07 -05:00
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$'
2020-01-28 21:07:36 -05:00
return {}
2020-01-07 07:45:07 -05:00
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
2021-10-11 05:30:43 -04:00
call fugitive#Autowrite()
2020-01-07 07:45:07 -05:00
let a:state.mode = 'init'
let a:state.from = ''
let a:state.to = ''
2021-10-11 05:30:43 -04:00
let exec = s:UserCommandList({'git': a:options.git, 'git_dir': a:options.git_dir}) + ['-c', 'diff.context=0']
2020-04-25 21:56:16 -04:00
let exec += a:options.flags + ['--no-pager', 'diff', '--no-ext-diff', '--no-color', '--no-prefix'] + argv
2020-01-07 07:45:07 -05:00
if prompt
2021-05-05 04:25:00 -04:00
let title = ':Git ' . s:fnameescape(a:options.flags + [a:options.subcommand] + a:options.subcommand_args)
2021-10-11 05:30:43 -04:00
return s:QuickfixStream(get(a:options, 'curwin') && a:line2 < 0 ? 0 : a:line2, 'difftool', title, exec, !a:bang, a:mods, s:function('s:ToolParse'), a:state)
2020-01-07 07:45:07 -05:00
else
let filename = ''
let cmd = []
let tabnr = tabpagenr() + 1
2021-10-11 05:30:43 -04:00
for line in s:SystemList(exec)[0]
2020-01-07 07:45:07 -05:00
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', []))))
2021-07-30 16:52:54 -04:00
call add(cmd, (i ? 'rightbelow' : 'leftabove') . ' vertical Gdiffsplit! ' . s:fnameescape(item.context.diff[i].filename))
2020-01-07 07:45:07 -05:00
endfor
call add(cmd, 'wincmd =')
let filename = item.filename
endif
endfor
endfor
return join(cmd, '|') . (empty(cmd) ? '' : '|' . tabnr . 'tabnext')
endif
endfunction
2020-04-25 21:56:16 -04:00
function! s:MergetoolSubcommand(line1, line2, range, bang, mods, options) abort
2021-10-11 05:30:43 -04:00
let dir = a:options.git_dir
2020-04-25 21:56:16 -04:00
exe s:DirCheck(dir)
2020-01-07 07:45:07 -05:00
let i = 0
let prompt = 1
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)})')
2021-05-05 04:25:00 -04:00
return s:ToolStream(a:line1, a:line2, a:range, a:bang, a:mods, a:options, ['--diff-filter=U'] + a:options.subcommand_args, state)
2020-01-07 07:45:07 -05:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:DifftoolSubcommand(line1, line2, range, bang, mods, options) abort
2021-10-11 05:30:43 -04:00
let dir = s:Dir(a:options)
2020-04-25 21:56:16 -04:00
exe s:DirCheck(dir)
2020-01-07 07:45:07 -05:00
let i = 0
2021-05-05 04:25:00 -04:00
let argv = copy(a:options.subcommand_args)
2020-01-07 07:45:07 -05:00
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
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
2020-04-25 21:56:16 -04:00
return s:ToolStream(a:line1, a:line2, a:range, a:bang, a:mods, a:options, argv, state)
2020-01-07 07:45:07 -05:00
endfunction
2018-08-25 12:13:42 -04:00
" Section: :Ggrep, :Glog
2018-06-14 06:31:12 -04:00
if !exists('g:fugitive_summary_format')
let g:fugitive_summary_format = '%s'
endif
2019-11-16 10:28:42 -05:00
function! fugitive#GrepComplete(A, L, P) abort
2019-08-22 11:36:17 -04:00
return s:CompleteSub('grep', a:A, a:L, a:P)
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#LogComplete(A, L, P) abort
2019-08-22 11:36:17 -04:00
return s:CompleteSub('log', a:A, a:L, a:P)
endfunction
2021-10-11 05:30:43 -04:00
function! s:GrepParseLine(options, quiet, dir, line) abort
if !a:quiet
echo a:line
endif
2019-08-22 11:36:17 -04:00
let entry = {'valid': 1}
2021-10-11 05:30:43 -04:00
let match = matchlist(a:line, '^\(.\{-\}\):\([1-9]\d*\):\([1-9]\d*:\)\=\(.*\)$')
if a:line =~# '^git: \|^usage: \|^error: \|^fatal: \|^BUG: '
return {'text': a:line}
elseif len(match)
2019-08-22 11:36:17 -04:00
let entry.module = match[1]
let entry.lnum = +match[2]
let entry.col = +match[3]
let entry.text = match[4]
2018-07-30 17:18:16 -04:00
else
2019-08-22 11:36:17 -04:00
let entry.module = matchstr(a:line, '\CBinary file \zs.*\ze matches$')
if len(entry.module)
let entry.text = 'Binary file'
let entry.valid = 0
endif
endif
2021-10-11 05:30:43 -04:00
if empty(entry.module) && !a:options.line_number
let match = matchlist(a:line, '^\(.\{-\}\):\(.*\)$')
if len(match)
let entry.module = match[1]
let entry.pattern = '\M^' . escape(match[2], '\.^$/') . '$'
endif
endif
if empty(entry.module) && a:options.name_count && a:line =~# ':\d\+$'
let entry.text = matchstr(a:line, '\d\+$')
let entry.module = strpart(a:line, 0, len(a:line) - len(entry.text) - 1)
endif
if empty(entry.module) && a:options.name_only
2019-08-22 11:36:17 -04:00
let entry.module = a:line
endif
if empty(entry.module)
return {'text': a:line}
endif
if entry.module !~# ':'
2022-08-08 09:45:56 -04:00
let entry.filename = s:PathJoin(a:options.prefix, entry.module)
2019-08-22 11:36:17 -04:00
else
let entry.filename = fugitive#Find(entry.module, a:dir)
endif
return entry
endfunction
2021-10-11 05:30:43 -04:00
let s:grep_combine_flags = '[aiIrhHEGPFnlLzocpWq]\{-\}'
function! s:GrepOptions(args, dir) abort
let options = {'name_only': 0, 'name_count': 0, 'line_number': 0}
let tree = s:Tree(a:dir)
let prefix = empty(tree) ? fugitive#Find(':0:', a:dir) :
2022-08-08 09:45:56 -04:00
\ s:cpath(getcwd(), tree) ? '' : s:VimSlash(tree . '/')
2021-10-11 05:30:43 -04:00
let options.prefix = prefix
for arg in a:args
if arg ==# '--'
break
endif
if arg =~# '^\%(-' . s:grep_combine_flags . 'c\|--count\)$'
let options.name_count = 1
endif
if arg =~# '^\%(-' . s:grep_combine_flags . 'n\|--line-number\)$'
let options.line_number = 1
elseif arg =~# '^\%(--no-line-number\)$'
let options.line_number = 0
endif
if arg =~# '^\%(-' . s:grep_combine_flags . '[lL]\|--files-with-matches\|--name-only\|--files-without-match\)$'
let options.name_only = 1
endif
if arg ==# '--cached'
let options.prefix = fugitive#Find(':0:', a:dir)
elseif arg ==# '--no-cached'
let options.prefix = prefix
endif
endfor
return options
endfunction
function! s:GrepCfile(result) abort
let options = s:GrepOptions(a:result.args, a:result)
let entry = s:GrepParseLine(options, 1, a:result, getline('.'))
if get(entry, 'col')
return [entry.filename, entry.lnum, "norm!" . entry.col . "|"]
elseif has_key(entry, 'lnum')
return [entry.filename, entry.lnum]
elseif has_key(entry, 'pattern')
return [entry.filename, '', 'silent /' . entry.pattern]
elseif has_key(entry, 'filename')
return [entry.filename]
else
return []
endif
endfunction
2020-04-25 21:56:16 -04:00
function! s:GrepSubcommand(line1, line2, range, bang, mods, options) abort
2021-10-11 05:30:43 -04:00
let args = copy(a:options.subcommand_args)
let handle = -1
let quiet = 0
let i = 0
while i < len(args) && args[i] !=# '--'
let partition = matchstr(args[i], '^-' . s:grep_combine_flags . '\ze[qzO]')
if len(partition) > 1
call insert(args, '-' . strpart(args[i], len(partition)), i+1)
let args[i] = partition
elseif args[i] =~# '^\%(-' . s:grep_combine_flags . '[eABC]\|--max-depth\|--context\|--after-context\|--before-context\|--threads\)$'
let i += 1
elseif args[i] =~# '^\%(-O\|--open-files-in-pager\)$'
let handle = 1
call remove(args, i)
continue
elseif args[i] =~# '^\%(-O\|--open-files-in-pager=\)'
let handle = 0
elseif args[i] =~# '^-[qz].'
let args[i] = '-' . args[i][2:-1]
let quiet = 1
elseif args[i] =~# '^\%(-[qz]\|--quiet\)$'
let quiet = 1
call remove(args, i)
continue
elseif args[i] =~# '^--no-quiet$'
let quiet = 0
elseif args[i] =~# '^\%(--heading\)$'
call remove(args, i)
continue
endif
let i += 1
endwhile
if handle < 0 ? !quiet : !handle
return {}
2020-04-25 21:56:16 -04:00
endif
2021-10-11 05:30:43 -04:00
call fugitive#Autowrite()
let listnr = get(a:options, 'curwin') && a:line2 < 0 ? 0 : a:line2
if s:HasOpt(args, '--no-line-number')
let lc = []
else
let lc = fugitive#GitVersion(2, 19) ? ['-n', '--column'] : ['-n']
endif
let cmd = ['grep', '--no-color', '--full-name'] + lc
let dir = s:Dir(a:options)
let options = s:GrepOptions(lc + args, dir)
2019-08-22 11:36:17 -04:00
if listnr > 0
exe listnr 'wincmd w'
else
call s:BlurStatus()
endif
2021-10-11 05:30:43 -04:00
let title = (listnr < 0 ? ':Ggrep ' : ':Glgrep ') . s:fnameescape(args)
call s:QuickfixCreate(listnr, {'title': title})
2019-08-22 11:36:17 -04:00
let tempfile = tempname()
2021-10-11 05:30:43 -04:00
let state = {
\ 'git': a:options.git,
\ 'flags': a:options.flags,
\ 'args': cmd + args,
\ 'git_dir': s:GitDir(a:options),
\ 'cwd': s:UserCommandCwd(a:options),
\ 'filetype': 'git',
\ 'mods': s:Mods(a:mods),
\ 'file': s:Resolve(tempfile)}
2019-11-16 10:28:42 -05:00
let event = listnr < 0 ? 'grep-fugitive' : 'lgrep-fugitive'
2022-08-08 09:45:56 -04:00
exe s:DoAutocmd('QuickFixCmdPre ' . event)
2021-05-05 04:25:00 -04:00
try
2021-10-11 05:30:43 -04:00
if !quiet && &more
let more = 1
set nomore
endif
if !quiet
echo title
endif
let list = s:SystemList(s:UserCommandList(a:options) + cmd + args)[0]
call writefile(list + [''], tempfile, 'b')
call s:RunSave(state)
call map(list, 's:GrepParseLine(options, ' . quiet . ', dir, v:val)')
call s:QuickfixSet(listnr, list, 'a')
let press_enter_shortfall = &cmdheight - len(list)
if press_enter_shortfall > 0 && !quiet
echo repeat("\n", press_enter_shortfall - 1)
2021-05-05 04:25:00 -04:00
endif
finally
2021-10-11 05:30:43 -04:00
if exists('l:more')
let &more = more
2021-05-05 04:25:00 -04:00
endif
endtry
2021-10-11 05:30:43 -04:00
call s:RunFinished(state)
2022-08-08 09:45:56 -04:00
exe s:DoAutocmd('QuickFixCmdPost ' . event)
2021-10-11 05:30:43 -04:00
if quiet
let bufnr = bufnr('')
exe s:QuickfixOpen(listnr, a:mods)
if bufnr != bufnr('') && !a:bang
wincmd p
endif
end
2019-08-22 11:36:17 -04:00
if !a:bang && !empty(list)
2021-10-11 05:30:43 -04:00
return 'silent ' . (listnr < 0 ? 'c' : 'l').'first'
2019-08-22 11:36:17 -04:00
else
2020-01-28 21:07:36 -05:00
return ''
2018-07-30 17:18:16 -04:00
endif
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#GrepCommand(line1, line2, range, bang, mods, arg) abort
return fugitive#Command(a:line1, a:line2, a:range, a:bang, a:mods,
2021-10-11 05:30:43 -04:00
\ "grep -O " . a:arg)
2020-04-25 21:56:16 -04:00
endfunction
2020-01-07 07:45:07 -05:00
let s:log_diff_context = '{"filename": fugitive#Find(v:val . from, a:dir), "lnum": get(offsets, v:key), "module": strpart(v:val, 0, len(a:state.base_module)) . from}'
function! s:LogFlushQueue(state, dir) abort
2019-08-22 11:36:17 -04:00
let queue = remove(a:state, 'queue')
2020-04-25 21:56:16 -04:00
if a:state.child_found && get(a:state, 'ignore_commit')
2019-08-22 11:36:17 -04:00
call remove(queue, 0)
2020-01-07 07:45:07 -05:00
elseif len(queue) && len(a:state.target) && len(get(a:state, 'parents', []))
let from = substitute(a:state.target, '^/', ':', '')
let offsets = []
let queue[0].context.diff = map(copy(a:state.parents), s:log_diff_context)
2019-08-22 11:36:17 -04:00
endif
if len(queue) && queue[-1] ==# {'text': ''}
call remove(queue, -1)
endif
return queue
endfunction
2018-06-14 06:31:12 -04:00
2021-10-11 05:30:43 -04:00
function! s:LogParse(state, dir, prefix, line) abort
2020-01-07 07:45:07 -05:00
if a:state.mode ==# 'hunk' && a:line =~# '^[-+ ]'
2019-08-22 11:36:17 -04:00
return []
endif
let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
if len(list)
2020-01-07 07:45:07 -05:00
let queue = s:LogFlushQueue(a:state, a:dir)
let a:state.mode = 'commit'
2021-10-11 05:30:43 -04:00
let a:state.base = a:prefix . list[2]
2020-01-07 07:45:07 -05:00
if len(list[1])
let [a:state.base_module; a:state.parents] = split(list[1], ' ')
else
let a:state.base_module = list[2]
let a:state.parents = []
2019-08-22 11:36:17 -04:00
endif
2020-01-07 07:45:07 -05:00
let a:state.message = list[3]
let a:state.from = ''
let a:state.to = ''
let context = {}
2019-08-22 11:36:17 -04:00
let a:state.queue = [{
\ 'valid': 1,
2020-01-07 07:45:07 -05:00
\ 'context': context,
2022-08-08 09:45:56 -04:00
\ 'filename': s:PathJoin(a:state.base, a:state.target),
2019-08-22 11:36:17 -04:00
\ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
\ 'text': a:state.message}]
let a:state.child_found = 0
return queue
elseif type(a:line) == type(0)
2020-01-07 07:45:07 -05:00
return s:LogFlushQueue(a:state, a:dir)
2019-08-22 11:36:17 -04:00
elseif a:line =~# '^diff'
2020-01-07 07:45:07 -05:00
let a:state.mode = 'diffhead'
let a:state.from = ''
let a:state.to = ''
elseif a:state.mode ==# 'diffhead' && a:line =~# '^--- \w/'
let a:state.from = a:line[6:-1]
let a:state.to = a:state.from
elseif a:state.mode ==# 'diffhead' && a:line =~# '^+++ \w/'
let a:state.to = a:line[6:-1]
if empty(get(a:state, 'from', ''))
let a:state.from = a:state.to
endif
elseif a:line =~# '^@@[^@]*+\d' && len(get(a:state, 'to', '')) && has_key(a:state, 'base')
let a:state.mode = 'hunk'
if empty(a:state.target) || a:state.target ==# '/' . a:state.to
if !a:state.child_found && len(a:state.queue) && a:state.queue[-1] ==# {'text': ''}
call remove(a:state.queue, -1)
endif
2019-08-22 11:36:17 -04:00
let a:state.child_found = 1
2020-01-07 07:45:07 -05:00
let offsets = map(split(matchstr(a:line, '^@\+ \zs[-+0-9, ]\+\ze @'), ' '), '+matchstr(v:val, "\\d\\+")')
let context = {}
if len(a:state.parents)
let from = ":" . a:state.from
let context.diff = map(copy(a:state.parents), s:log_diff_context)
endif
2019-08-22 11:36:17 -04:00
call add(a:state.queue, {
\ 'valid': 1,
2020-01-07 07:45:07 -05:00
\ 'context': context,
2022-08-08 09:45:56 -04:00
\ 'filename': s:VimSlash(a:state.base . '/' . a:state.to),
2020-01-07 07:45:07 -05:00
\ 'module': a:state.base_module . ':' . a:state.to,
\ 'lnum': offsets[-1],
2019-08-22 11:36:17 -04:00
\ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
endif
elseif a:state.follow &&
\ a:line =~# '^ \%(mode change \d\|\%(create\|delete\) mode \d\|\%(rename\|copy\|rewrite\) .* (\d\+%)$\)'
2022-08-08 09:45:56 -04:00
let rename = matchstr(a:line, '^ \%(copy\|rename\) \zs.* => .*\ze (\d\+%)$')
2019-08-22 11:36:17 -04:00
if len(rename)
let rename = rename =~# '{.* => .*}' ? rename : '{' . rename . '}'
if a:state.target ==# simplify('/' . substitute(rename, '{.* => \(.*\)}', '\1', ''))
let a:state.target = simplify('/' . substitute(rename, '{\(.*\) => .*}', '\1', ''))
2018-06-14 06:31:12 -04:00
endif
endif
2019-08-22 11:36:17 -04:00
if !get(a:state, 'ignore_summary')
call add(a:state.queue, {'text': a:line})
2018-06-14 06:31:12 -04:00
endif
2020-01-07 07:45:07 -05:00
elseif a:state.mode ==# 'commit' || a:state.mode ==# 'init'
2019-08-22 11:36:17 -04:00
call add(a:state.queue, {'text': a:line})
endif
return []
2018-06-14 06:31:12 -04:00
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
2019-08-22 11:36:17 -04:00
let dir = s:Dir()
exe s:DirCheck(dir)
let listnr = a:type =~# '^l' ? 0 : -1
2021-05-05 04:25:00 -04:00
let [args, after] = s:SplitExpandChain('log ' . a:args, s:Tree(dir))
call remove(args, 0)
2019-08-22 11:36:17 -04:00
let split = index(args, '--')
if split > 0
let paths = args[split : -1]
let args = args[0 : split - 1]
elseif split == 0
let paths = args
let args = []
else
let paths = []
endif
if a:line1 == 0 && a:count
let path = fugitive#Path(bufname(a:count), '/', dir)
2020-01-07 07:45:07 -05:00
let titlepre = ':0,' . a:count
2019-08-22 11:36:17 -04:00
elseif a:count >= 0
let path = fugitive#Path(@%, '/', dir)
2020-01-07 07:45:07 -05:00
let titlepre = a:count == 0 ? ':0,' . bufnr('') : ':'
2019-08-22 11:36:17 -04:00
else
2020-01-07 07:45:07 -05:00
let titlepre = ':'
let path = ''
2019-08-22 11:36:17 -04:00
endif
let range = ''
2020-01-07 07:45:07 -05:00
let extra_args = []
let extra_paths = []
let state = {'mode': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
2019-08-22 11:36:17 -04:00
if path =~# '^/\.git\%(/\|$\)\|^$'
2018-06-14 06:31:12 -04:00
let path = ''
2019-08-22 11:36:17 -04:00
elseif a:line1 == 0
let range = "0," . (a:count ? a:count : bufnr(''))
2020-01-07 07:45:07 -05:00
let extra_paths = ['.' . path]
2019-08-22 11:36:17 -04:00
if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
let state.follow = 1
if !s:HasOpt(args, '--follow')
2020-01-07 07:45:07 -05:00
call insert(extra_args, '--follow')
2019-08-22 11:36:17 -04:00
endif
if !s:HasOpt(args, '--summary')
2020-01-07 07:45:07 -05:00
call insert(extra_args, '--summary')
2019-08-22 11:36:17 -04:00
let state.ignore_summary = 1
endif
endif
2020-04-25 21:56:16 -04:00
let state.ignore_commit = 1
2019-08-22 11:36:17 -04:00
elseif a:count > 0
if !s:HasOpt(args, '--merges', '--no-merges')
2020-01-07 07:45:07 -05:00
call insert(extra_args, '--no-merges')
2019-08-22 11:36:17 -04:00
endif
call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
2020-04-25 21:56:16 -04:00
let state.ignore_commit = 1
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
if len(path) && empty(filter(copy(args), 'v:val =~# "^[^-]"'))
let owner = s:Owner(@%, dir)
2018-08-25 12:13:42 -04:00
if len(owner)
2021-05-05 04:25:00 -04:00
call add(args, owner . (owner =~# '^\x\{40,}' ? '' : '^{}'))
2018-06-14 06:31:12 -04:00
endif
endif
2020-01-07 07:45:07 -05:00
if empty(extra_paths)
2019-08-22 11:36:17 -04:00
let path = ''
2018-08-25 12:13:42 -04:00
endif
2019-08-22 11:36:17 -04:00
if s:HasOpt(args, '-g', '--walk-reflogs')
2020-01-07 07:45:07 -05:00
let format = "%gd %P\t%H %gs"
2019-08-22 11:36:17 -04:00
else
2020-01-07 07:45:07 -05:00
let format = "%h %P\t%H " . g:fugitive_summary_format
2018-08-25 12:13:42 -04:00
endif
2019-08-22 11:36:17 -04:00
let cmd = ['--no-pager']
2021-05-05 04:25:00 -04:00
call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'] +
2019-08-22 11:36:17 -04:00
\ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
2020-01-07 07:45:07 -05:00
\ args + extra_args + paths + extra_paths)
2019-08-22 11:36:17 -04:00
let state.target = path
2020-01-07 07:45:07 -05:00
let title = titlepre . (listnr < 0 ? 'Gclog ' : 'Gllog ') . s:fnameescape(args + paths)
2021-10-11 05:30:43 -04:00
return s:QuickfixStream(listnr, 'log', title, s:UserCommandList(dir) + cmd, !a:bang, a:mods, s:function('s:LogParse'), state, dir, s:DirUrlPrefix(dir)) . after
2018-06-14 06:31:12 -04:00
endfunction
2018-08-25 12:13:42 -04:00
" Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
2018-06-14 06:31:12 -04:00
function! s:UsableWin(nr) abort
2019-08-22 11:36:17 -04:00
return a:nr && !getwinvar(a:nr, '&previewwindow') && !getwinvar(a:nr, '&winfixwidth') &&
2019-03-08 06:04:56 -05:00
\ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
2019-01-08 05:11:54 -05:00
\ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
2021-05-05 04:25:00 -04:00
\ index(['nofile','help','quickfix', 'terminal'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
endfunction
function! s:ArgSplit(string) abort
let string = a:string
let args = []
while string =~# '\S'
2022-08-08 09:45:56 -04:00
let arg = matchstr(string, '^\s*\%(\\.\|\S\)\+')
2021-05-05 04:25:00 -04:00
let string = strpart(string, len(arg))
let arg = substitute(arg, '^\s\+', '', '')
2022-08-08 09:45:56 -04:00
call add(args, substitute(arg, '\\\+[|" ]', '\=submatch(0)[len(submatch(0))/2 : -1]', 'g'))
2021-05-05 04:25:00 -04:00
endwhile
return args
2018-06-14 06:31:12 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:PlusEscape(string) abort
return substitute(a:string, '\\*[|" ]', '\=repeat("\\", len(submatch(0))).submatch(0)', 'g')
endfunction
function! s:OpenParse(string, wants_cmd, wants_multiple) abort
2019-11-16 10:28:42 -05:00
let opts = []
let cmds = []
2021-05-05 04:25:00 -04:00
let args = s:ArgSplit(a:string)
2019-11-16 10:28:42 -05:00
while !empty(args)
if args[0] =~# '^++'
2022-08-08 09:45:56 -04:00
call add(opts, ' ' . s:PlusEscape(remove(args, 0)))
elseif a:wants_cmd && args[0] ==# '+'
call remove(args, 0)
call add(cmds, '$')
2019-11-16 10:28:42 -05:00
elseif a:wants_cmd && args[0] =~# '^+'
call add(cmds, remove(args, 0)[1:-1])
else
break
endif
2018-07-30 17:18:16 -04:00
endwhile
2022-08-08 09:45:56 -04:00
if !a:wants_multiple && empty(args)
let args = ['>:']
2018-07-30 17:18:16 -04:00
endif
2019-11-16 10:28:42 -05:00
let dir = s:Dir()
2022-08-08 09:45:56 -04:00
let wants_cmd = a:wants_cmd
let urls = []
for arg in args
let [url, lnum] = s:OpenExpand(dir, arg, wants_cmd)
if lnum
call insert(cmds, lnum)
endif
call add(urls, url)
let wants_cmd = 0
endfor
let pre = join(opts, '')
if len(cmds) > 1
let pre .= ' +' . s:PlusEscape(join(map(cmds, '"exe ".string(v:val)'), '|'))
elseif len(cmds)
let pre .= ' +' . s:PlusEscape(cmds[0])
endif
return [a:wants_multiple ? urls : urls[0], pre]
endfunction
2019-11-16 10:28:42 -05:00
2022-08-08 09:45:56 -04:00
function! s:OpenExpand(dir, file, wants_cmd) abort
if a:file ==# '-'
let result = fugitive#Result()
if has_key(result, 'file')
let efile = result.file
else
throw 'fugitive: no previous command output'
endif
else
let efile = s:Expand(a:file)
endif
let url = s:Generate(efile, a:dir)
if a:wants_cmd && a:file[0] ==# '>' && efile[0] !=# '>' && get(b:, 'fugitive_type', '') isnot# 'tree' && &filetype !=# 'netrw'
2019-11-16 10:28:42 -05:00
let line = line('.')
2022-08-08 09:45:56 -04:00
if s:Slash(expand('%:p')) !=# s:Slash(url)
2019-11-16 10:28:42 -05:00
let diffcmd = 'diff'
let from = s:DirRev(@%)[1]
let to = s:DirRev(url)[1]
if empty(from) && empty(to)
let diffcmd = 'diff-files'
let args = ['--', expand('%:p'), url]
elseif empty(to)
let args = [from, '--', url]
elseif empty(from)
let args = [to, '--', expand('%:p')]
let reverse = 1
else
let args = [from, to]
endif
2022-08-08 09:45:56 -04:00
let [res, exec_error] = s:LinesError([a:dir, diffcmd, '-U0'] + args)
2019-11-16 10:28:42 -05:00
if !exec_error
call filter(res, 'v:val =~# "^@@ "')
call map(res, 'substitute(v:val, ''[-+]\d\+\zs '', ",1 ", "g")')
call map(res, 'matchlist(v:val, ''^@@ -\(\d\+\),\(\d\+\) +\(\d\+\),\(\d\+\) @@'')[1:4]')
if exists('reverse')
call map(res, 'v:val[2:3] + v:val[0:1]')
endif
call filter(res, 'v:val[0] < '.line('.'))
let hunk = get(res, -1, [0,0,0,0])
if hunk[0] + hunk[1] > line('.')
let line = hunk[2] + max([1 - hunk[3], 0])
else
let line = hunk[2] + max([hunk[3], 1]) + line('.') - hunk[0] - max([hunk[1], 1])
endif
endif
endif
2022-08-08 09:45:56 -04:00
return [url, line]
2019-11-16 10:28:42 -05:00
endif
2022-08-08 09:45:56 -04:00
return [url, 0]
2018-07-30 17:18:16 -04:00
endfunction
2020-12-04 16:15:32 -05:00
function! fugitive#DiffClose() abort
2019-08-22 11:36:17 -04:00
let mywinnr = winnr()
for winnr in [winnr('#')] + range(winnr('$'),1,-1)
if winnr != mywinnr && getwinvar(winnr,'&diff')
execute winnr.'wincmd w'
close
if winnr('$') > 1
wincmd p
endif
endif
endfor
diffoff!
endfunction
2018-08-25 12:13:42 -04:00
function! s:BlurStatus() abort
2019-01-08 05:11:54 -05:00
if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
2018-07-30 17:18:16 -04:00
let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
if len(winnrs)
exe winnrs[0].'wincmd w'
else
2022-08-08 09:45:56 -04:00
belowright new +setl\ bufhidden=delete
2018-07-30 17:18:16 -04:00
endif
if &diff
2020-12-04 16:15:32 -05:00
call fugitive#DiffClose()
2018-06-14 06:31:12 -04:00
endif
endif
2018-08-25 12:13:42 -04:00
endfunction
2021-07-30 16:52:54 -04:00
let s:bang_edits = {'split': 'Git', 'vsplit': 'vertical Git', 'tabedit': 'tab Git', 'pedit': 'Git!'}
2021-10-11 05:30:43 -04:00
function! fugitive#Open(cmd, bang, mods, arg, ...) abort
2021-05-05 04:25:00 -04:00
exe s:VersionCheck()
2018-06-14 06:31:12 -04:00
if a:bang
2021-05-05 04:25:00 -04:00
return 'echoerr ' . string(':G' . a:cmd . '! for temp buffer output has been replaced by :' . get(s:bang_edits, a:cmd, 'Git') . ' --paginate')
2018-06-14 06:31:12 -04:00
endif
2022-08-08 09:45:56 -04:00
let mods = s:Mods(a:mods)
if a:cmd ==# 'edit'
call s:BlurStatus()
endif
try
let [file, pre] = s:OpenParse(a:arg, 1, 0)
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
return mods . a:cmd . pre . ' ' . s:fnameescape(file)
endfunction
function! fugitive#DropCommand(line1, count, range, bang, mods, arg, ...) abort
exe s:VersionCheck()
2019-08-22 11:36:17 -04:00
let mods = s:Mods(a:mods)
2018-06-14 06:31:12 -04:00
try
2022-08-08 09:45:56 -04:00
let [files, pre] = s:OpenParse(a:arg, 1, 1)
2018-06-14 06:31:12 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-06-14 06:31:12 -04:00
endtry
2022-08-08 09:45:56 -04:00
if empty(files)
return 'drop'
2018-06-14 06:31:12 -04:00
endif
2022-08-08 09:45:56 -04:00
call s:BlurStatus()
return mods . 'drop' . ' ' . s:fnameescape(files) . substitute(pre, '^ *+', '|', '')
2018-06-14 06:31:12 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! s:ReadPrepare(line1, count, range, mods) abort
2019-08-22 11:36:17 -04:00
let mods = s:Mods(a:mods)
let after = a:count
2018-07-30 17:18:16 -04:00
if a:count < 0
let delete = 'silent 1,' . line('$') . 'delete_|'
let after = line('$')
elseif a:range == 2
2019-08-22 11:36:17 -04:00
let delete = 'silent ' . a:line1 . ',' . a:count . 'delete_|'
2018-07-30 17:18:16 -04:00
else
let delete = ''
endif
2020-04-25 21:56:16 -04:00
if foldlevel(after)
let pre = after . 'foldopen!|'
else
let pre = ''
endif
2021-10-11 05:30:43 -04:00
return [pre . 'keepalt ' . mods . after . 'read', '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')]
2020-04-25 21:56:16 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#ReadCommand(line1, count, range, bang, mods, arg, ...) abort
2021-05-05 04:25:00 -04:00
exe s:VersionCheck()
2020-04-25 21:56:16 -04:00
let [read, post] = s:ReadPrepare(a:line1, a:count, a:range, a:mods)
2018-07-30 17:18:16 -04:00
try
2022-08-08 09:45:56 -04:00
let [file, pre] = s:OpenParse(a:arg, 0, 0)
2018-07-30 17:18:16 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-07-30 17:18:16 -04:00
endtry
2020-04-25 21:56:16 -04:00
if file =~# '^fugitive:' && a:count is# 0
2021-05-05 04:25:00 -04:00
return 'exe ' .string('keepalt ' . s:Mods(a:mods) . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
2019-01-08 05:11:54 -05:00
endif
2021-10-11 05:30:43 -04:00
return read . ' ' . pre . ' ' . s:fnameescape(file) . post
2018-06-14 06:31:12 -04:00
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#EditComplete(A, L, P) abort
if a:A =~# '^>'
return map(s:FilterEscape(s:CompleteHeads(s:Dir()), a:A[1:-1]), "'>' . v:val")
2018-06-14 06:31:12 -04:00
else
2019-05-17 10:09:13 -04:00
return fugitive#CompleteObject(a:A, a:L, a:P)
2018-06-14 06:31:12 -04:00
endif
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#ReadComplete(A, L, P) abort
2022-08-08 09:45:56 -04:00
return fugitive#EditComplete(a:A, a:L, a:P)
2019-11-16 10:28:42 -05:00
endfunction
2018-06-14 06:31:12 -04:00
2018-08-25 12:13:42 -04:00
" Section: :Gwrite, :Gwq
2018-06-14 06:31:12 -04:00
2021-10-11 05:30:43 -04:00
function! fugitive#WriteCommand(line1, line2, range, bang, mods, arg, ...) abort
2021-05-05 04:25:00 -04:00
exe s:VersionCheck()
if s:cpath(expand('%:p'), fugitive#Find('.git/COMMIT_EDITMSG')) && empty(a:arg)
2020-12-04 16:15:32 -05:00
return (empty($GIT_INDEX_FILE) ? 'write|bdelete' : 'wq') . (a:bang ? '!' : '')
2021-05-05 04:25:00 -04:00
elseif get(b:, 'fugitive_type', '') ==# 'index' && empty(a:arg)
2020-04-25 21:56:16 -04:00
return 'Git commit'
2020-01-28 21:07:36 -05:00
elseif &buftype ==# 'nowrite' && getline(4) =~# '^[+-]\{3\} '
return 'echoerr ' . string('fugitive: :Gwrite from :Git diff has been removed in favor of :Git add --edit')
2018-06-14 06:31:12 -04:00
endif
let mytab = tabpagenr()
let mybufnr = bufnr('')
2021-05-05 04:25:00 -04:00
let args = s:ArgSplit(a:arg)
let after = ''
if get(args, 0) =~# '^+'
let after = '|' . remove(args, 0)[1:-1]
endif
2019-08-22 11:36:17 -04:00
try
2021-05-05 04:25:00 -04:00
let file = len(args) ? s:Generate(s:Expand(join(args, ' '))) : fugitive#Real(@%)
2019-08-22 11:36:17 -04:00
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
2018-08-25 12:13:42 -04:00
if empty(file)
2018-06-14 06:31:12 -04:00
return 'echoerr '.string('fugitive: cannot determine file path')
endif
2018-08-25 12:13:42 -04:00
if file =~# '^fugitive:'
2019-05-17 10:09:13 -04:00
return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
2018-06-14 06:31:12 -04:00
endif
2019-11-16 10:28:42 -05:00
exe s:DirCheck()
2019-08-22 11:36:17 -04:00
let always_permitted = s:cpath(fugitive#Real(@%), file) && empty(s:DirCommitFile(@%)[1])
2019-05-17 10:09:13 -04:00
if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
2018-06-14 06:31:12 -04:00
let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
return 'echoerr v:errmsg'
endif
let treebufnr = 0
for nr in range(1,bufnr('$'))
if fnamemodify(bufname(nr),':p') ==# file
let treebufnr = nr
endif
endfor
if treebufnr > 0 && treebufnr != bufnr('')
let temp = tempname()
2019-08-22 11:36:17 -04:00
silent execute 'keepalt %write '.temp
2018-06-14 06:31:12 -04:00
for tab in [mytab] + range(1,tabpagenr('$'))
for winnr in range(1,tabpagewinnr(tab,'$'))
if tabpagebuflist(tab)[winnr-1] == treebufnr
execute 'tabnext '.tab
if winnr != winnr()
execute winnr.'wincmd w'
let restorewinnr = 1
endif
try
let lnum = line('.')
let last = line('$')
silent execute '$read '.temp
silent execute '1,'.last.'delete_'
silent write!
silent execute lnum
2019-08-22 11:36:17 -04:00
diffupdate
2018-06-14 06:31:12 -04:00
let did = 1
finally
if exists('restorewinnr')
wincmd p
endif
execute 'tabnext '.mytab
endtry
2019-08-22 11:36:17 -04:00
break
2018-06-14 06:31:12 -04:00
endif
endfor
endfor
if !exists('did')
call writefile(readfile(temp,'b'),file,'b')
endif
else
2018-08-25 12:13:42 -04:00
execute 'write! '.s:fnameescape(file)
2018-06-14 06:31:12 -04:00
endif
2021-10-11 05:30:43 -04:00
let message = s:ChompStderr(['add'] + (a:bang ? ['--force'] : []) + ['--', file])
if len(message)
let v:errmsg = 'fugitive: '.message
2018-06-14 06:31:12 -04:00
return 'echoerr v:errmsg'
endif
2018-08-25 12:13:42 -04:00
if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
2019-03-08 06:04:56 -05:00
setlocal nomodified
2018-06-14 06:31:12 -04:00
endif
2021-05-05 04:25:00 -04:00
let one = fugitive#Find(':1:'.file)
let two = fugitive#Find(':2:'.file)
let three = fugitive#Find(':3:'.file)
2018-06-14 06:31:12 -04:00
for nr in range(1,bufnr('$'))
let name = fnamemodify(bufname(nr), ':p')
if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three)
execute nr.'bdelete'
endif
endfor
unlet! restorewinnr
2021-05-05 04:25:00 -04:00
let zero = fugitive#Find(':0:'.file)
2022-08-08 09:45:56 -04:00
exe s:DoAutocmd('BufWritePost ' . s:fnameescape(zero))
2018-06-14 06:31:12 -04:00
for tab in range(1,tabpagenr('$'))
for winnr in range(1,tabpagewinnr(tab,'$'))
let bufnr = tabpagebuflist(tab)[winnr-1]
let bufname = fnamemodify(bufname(bufnr), ':p')
if bufname ==# zero && bufnr != mybufnr
execute 'tabnext '.tab
if winnr != winnr()
execute winnr.'wincmd w'
let restorewinnr = 1
endif
try
let lnum = line('.')
let last = line('$')
silent execute '$read '.s:fnameescape(file)
silent execute '1,'.last.'delete_'
silent execute lnum
2019-03-08 06:04:56 -05:00
setlocal nomodified
2018-06-14 06:31:12 -04:00
diffupdate
finally
if exists('restorewinnr')
wincmd p
endif
execute 'tabnext '.mytab
endtry
break
endif
endfor
endfor
2021-10-11 05:30:43 -04:00
call fugitive#DidChange()
2022-08-08 09:45:56 -04:00
return 'checktime' . after
2018-06-14 06:31:12 -04:00
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#WqCommand(...) abort
let bang = a:4 ? '!' : ''
2020-12-04 16:15:32 -05:00
if s:cpath(expand('%:p'), fugitive#Find('.git/COMMIT_EDITMSG'))
2018-06-14 06:31:12 -04:00
return 'wq'.bang
endif
2019-11-16 10:28:42 -05:00
let result = call('fugitive#WriteCommand', a:000)
2018-06-14 06:31:12 -04:00
if result =~# '^\%(write\|wq\|echoerr\)'
return s:sub(result,'^write','wq')
else
return result.'|quit'.bang
endif
endfunction
2020-04-25 21:56:16 -04:00
" Section: :Git push, :Git fetch
2018-06-14 06:31:12 -04:00
2021-10-11 05:30:43 -04:00
function! s:CompletePush(A, L, P, ...) abort
let dir = a:0 ? a:1 : s:Dir()
let remote = matchstr(a:L, '\u\w*[! ] *.\{-\}\s\@<=\zs[^-[:space:]]\S*\ze ')
if empty(remote)
let matches = s:LinesError([dir, 'remote'])[0]
elseif a:A =~# ':'
let lead = matchstr(a:A, '^[^:]*:')
let matches = s:LinesError([dir, 'ls-remote', remote])[0]
call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
call map(matches, 'lead . s:sub(v:val, "^.*\t", "")')
else
let matches = s:CompleteHeads(dir)
2022-08-08 09:45:56 -04:00
if a:A =~# '^[\''"]\=+'
call map(matches, '"+" . v:val')
endif
2021-10-11 05:30:43 -04:00
endif
return s:FilterEscape(matches, a:A)
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#PushComplete(A, L, P, ...) abort
2021-10-11 05:30:43 -04:00
return s:CompleteSub('push', a:A, a:L, a:P, function('s:CompletePush'), a:000)
2019-08-22 11:36:17 -04:00
endfunction
2020-04-25 21:56:16 -04:00
function! fugitive#FetchComplete(A, L, P, ...) abort
return s:CompleteSub('fetch', a:A, a:L, a:P, function('s:CompleteRemote'), a:000)
2019-08-22 11:36:17 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:PushSubcommand(...) abort
return {'no_more': 1}
endfunction
function! s:FetchSubcommand(...) abort
return {'no_more': 1}
endfunction
2018-08-25 12:13:42 -04:00
" Section: :Gdiff
2018-06-14 06:31:12 -04:00
augroup fugitive_diff
autocmd!
2020-06-21 11:50:44 -04:00
autocmd BufWinLeave * nested
2018-06-14 06:31:12 -04:00
\ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
2019-03-08 06:04:56 -05:00
\ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
2018-06-14 06:31:12 -04:00
\ endif
2020-06-21 11:50:44 -04:00
autocmd BufWinEnter * nested
2018-06-14 06:31:12 -04:00
\ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 1 |
\ call s:diffoff() |
\ endif
augroup END
function! s:can_diffoff(buf) abort
return getwinvar(bufwinnr(a:buf), '&diff') &&
\ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
endfunction
function! fugitive#CanDiffoff(buf) abort
2019-03-08 06:04:56 -05:00
return s:can_diffoff(bufnr(a:buf))
2018-06-14 06:31:12 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:DiffModifier(count, default) abort
2018-06-14 06:31:12 -04:00
let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
2019-08-22 11:36:17 -04:00
return ''
2018-06-14 06:31:12 -04:00
elseif &diffopt =~# 'vertical'
2019-08-22 11:36:17 -04:00
return 'vertical '
2021-10-11 05:30:43 -04:00
elseif !get(g:, 'fugitive_diffsplit_directional_fit', a:default)
return ''
2018-06-14 06:31:12 -04:00
elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
2019-08-22 11:36:17 -04:00
return ''
2018-06-14 06:31:12 -04:00
else
2019-08-22 11:36:17 -04:00
return 'vertical '
2018-06-14 06:31:12 -04:00
endif
endfunction
function! s:diff_window_count() abort
let c = 0
for nr in range(1,winnr('$'))
let c += getwinvar(nr,'&diff')
endfor
return c
endfunction
function! s:diffthis() abort
if !&diff
2022-08-08 09:45:56 -04:00
let w:fugitive_diff_restore = 1
2018-06-14 06:31:12 -04:00
diffthis
endif
endfunction
function! s:diffoff() abort
2020-12-04 16:15:32 -05:00
unlet! w:fugitive_diff_restore
diffoff
2018-06-14 06:31:12 -04:00
endfunction
function! s:diffoff_all(dir) abort
let curwin = winnr()
for nr in range(1,winnr('$'))
2019-11-30 07:06:56 -05:00
if getwinvar(nr, '&diff') && !empty(getwinvar(nr, 'fugitive_diff_restore'))
2020-12-04 16:15:32 -05:00
call setwinvar(nr, 'fugitive_diff_restore', '')
2018-06-14 06:31:12 -04:00
endif
endfor
2020-12-04 16:15:32 -05:00
if curwin != winnr()
execute curwin.'wincmd w'
endif
diffoff!
2018-06-14 06:31:12 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:IsConflicted() abort
2021-10-11 05:30:43 -04:00
return len(@%) && !empty(s:ChompDefault('', ['ls-files', '--unmerged', '--', expand('%:p')]))
2019-08-22 11:36:17 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, ...) abort
2021-05-05 04:25:00 -04:00
exe s:VersionCheck()
let args = s:ArgSplit(a:arg)
2018-06-14 06:31:12 -04:00
let post = ''
2021-10-11 05:30:43 -04:00
let autodir = a:autodir
while get(args, 0, '') =~# '^++'
if args[0] =~? '^++novertical$'
let autodir = 0
else
return 'echoerr ' . string('fugitive: unknown option ' . args[0])
endif
call remove(args, 0)
endwhile
2018-06-14 06:31:12 -04:00
if get(args, 0) =~# '^+'
let post = remove(args, 0)[1:-1]
endif
2019-11-16 10:28:42 -05:00
if exists(':DiffGitCached') && empty(args)
return s:Mods(a:mods) . 'DiffGitCached' . (len(post) ? '|' . post : '')
endif
2018-07-30 17:18:16 -04:00
let commit = s:DirCommitFile(@%)[1]
2019-08-22 11:36:17 -04:00
if a:mods =~# '\<tab\>'
let mods = substitute(a:mods, '\<tab\>', '', 'g')
2019-11-16 10:28:42 -05:00
let pre = 'tab split'
2019-08-22 11:36:17 -04:00
else
let mods = 'keepalt ' . a:mods
2019-11-16 10:28:42 -05:00
let pre = ''
2019-08-22 11:36:17 -04:00
endif
2018-07-30 17:18:16 -04:00
let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
2021-10-11 05:30:43 -04:00
if (empty(args) || args[0] =~# '^>\=:$') && a:keepfocus
2019-11-16 10:28:42 -05:00
exe s:DirCheck()
2021-05-05 04:25:00 -04:00
if commit =~# '^1\=$' && s:IsConflicted()
2019-08-22 11:36:17 -04:00
let parents = [s:Relative(':2:'), s:Relative(':3:')]
elseif empty(commit)
let parents = [s:Relative(':0:')]
elseif commit =~# '^\d\=$'
2021-05-05 04:25:00 -04:00
let parents = [s:Relative('@:')]
2019-08-22 11:36:17 -04:00
elseif commit =~# '^\x\x\+$'
let parents = s:LinesError(['rev-parse', commit . '^@'])[0]
call map(parents, 's:Relative(v:val . ":")')
endif
endif
try
if exists('parents') && len(parents) > 1
2019-11-16 10:28:42 -05:00
exe pre
2021-10-11 05:30:43 -04:00
let mods = (autodir ? s:DiffModifier(len(parents) + 1, empty(args) || args[0] =~# '^>') : '') . s:Mods(mods, 'leftabove')
2019-08-22 11:36:17 -04:00
let nr = bufnr('')
2021-07-30 16:52:54 -04:00
if len(parents) > 1 && !&equalalways
let equalalways = 0
set equalalways
endif
2021-05-05 04:25:00 -04:00
execute mods 'split' s:fnameescape(fugitive#Find(parents[0]))
2019-08-22 11:36:17 -04:00
call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
let nr2 = bufnr('')
call s:diffthis()
exe back
call s:Map('n', 'd2o', ':diffget '.nr2.'<Bar>diffupdate<CR>', '<silent>')
let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
for i in range(len(parents)-1, 1, -1)
2021-05-05 04:25:00 -04:00
execute mods 'split' s:fnameescape(fugitive#Find(parents[i]))
2019-08-22 11:36:17 -04:00
call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
let nrx = bufnr('')
call s:diffthis()
exe back
call s:Map('n', 'd' . (i + 2) . 'o', ':diffget '.nrx.'<Bar>diffupdate<CR>', '<silent>')
endfor
call s:diffthis()
2018-06-14 06:31:12 -04:00
return post
2019-08-22 11:36:17 -04:00
elseif len(args)
let arg = join(args, ' ')
if arg ==# ''
return post
2019-11-16 10:28:42 -05:00
elseif arg ==# ':/'
exe s:DirCheck()
2019-08-22 11:36:17 -04:00
let file = s:Relative()
elseif arg ==# ':'
2019-11-16 10:28:42 -05:00
exe s:DirCheck()
2021-10-11 05:30:43 -04:00
let file = len(commit) ? s:Relative() : s:Relative(s:IsConflicted() ? ':1:' : ':0:')
2019-08-22 11:36:17 -04:00
elseif arg =~# '^:\d$'
2019-11-16 10:28:42 -05:00
exe s:DirCheck()
2019-08-22 11:36:17 -04:00
let file = s:Relative(arg . ':')
2019-11-30 07:06:56 -05:00
elseif arg =~# '^[~^]\d*$'
return 'echoerr ' . string('fugitive: change ' . arg . ' to !' . arg . ' to diff against ancestor')
2019-08-22 11:36:17 -04:00
else
try
let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
catch /^fugitive:/
return 'echoerr ' . string(v:exception)
endtry
endif
2021-10-11 05:30:43 -04:00
if a:keepfocus || arg =~# '^>'
let mods = s:Mods(a:mods, 'leftabove')
else
let mods = s:Mods(a:mods)
endif
elseif exists('parents')
let file = get(parents, -1, s:Relative(repeat('0', 40). ':'))
let mods = s:Mods(a:mods, 'leftabove')
2019-08-22 11:36:17 -04:00
elseif len(commit)
2018-08-25 12:13:42 -04:00
let file = s:Relative()
2021-10-11 05:30:43 -04:00
let mods = s:Mods(a:mods, 'rightbelow')
2019-08-22 11:36:17 -04:00
elseif s:IsConflicted()
let file = s:Relative(':1:')
2021-10-11 05:30:43 -04:00
let mods = s:Mods(a:mods, 'leftabove')
if get(g:, 'fugitive_legacy_commands', 1)
let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
endif
2018-06-14 06:31:12 -04:00
else
2019-11-16 10:28:42 -05:00
exe s:DirCheck()
2019-08-22 11:36:17 -04:00
let file = s:Relative(':0:')
2021-10-11 05:30:43 -04:00
let mods = s:Mods(a:mods, 'leftabove')
2018-06-14 06:31:12 -04:00
endif
2018-07-30 17:18:16 -04:00
let spec = s:Generate(file)
2019-08-22 11:36:17 -04:00
if spec =~# '^fugitive:' && empty(s:DirCommitFile(spec)[2])
2022-08-08 09:45:56 -04:00
let spec = s:VimSlash(spec . s:Relative('/'))
2018-06-14 06:31:12 -04:00
endif
2019-11-16 10:28:42 -05:00
exe pre
2022-08-08 09:45:56 -04:00
let w:fugitive_diff_restore = 1
2021-10-11 05:30:43 -04:00
let mods = (autodir ? s:DiffModifier(2, empty(args) || args[0] =~# '^>') : '') . mods
2019-08-22 11:36:17 -04:00
if &diffopt =~# 'vertical'
let diffopt = &diffopt
set diffopt-=vertical
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
execute mods 'diffsplit' s:fnameescape(spec)
2022-08-08 09:45:56 -04:00
let w:fugitive_diff_restore = 1
2018-06-14 06:31:12 -04:00
let winnr = winnr()
if getwinvar('#', '&diff')
2019-08-22 11:36:17 -04:00
if a:keepfocus
exe back
2018-06-14 06:31:12 -04:00
endif
endif
return post
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
finally
2021-07-30 16:52:54 -04:00
if exists('l:equalalways')
2022-08-08 09:45:56 -04:00
let &g:equalalways = equalalways
2021-07-30 16:52:54 -04:00
endif
2019-08-22 11:36:17 -04:00
if exists('diffopt')
let &diffopt = diffopt
endif
2018-06-14 06:31:12 -04:00
endtry
endfunction
2021-05-05 04:25:00 -04:00
" Section: :GMove, :GRemove
2018-06-14 06:31:12 -04:00
function! s:Move(force, rename, destination) abort
2019-11-16 10:28:42 -05:00
let dir = s:Dir()
exe s:DirCheck(dir)
if s:DirCommitFile(@%)[1] !~# '^0\=$' || empty(@%)
return 'echoerr ' . string('fugitive: mv not supported for this buffer')
endif
2021-10-11 05:30:43 -04:00
if a:rename
let default_root = expand('%:p:s?[\/]$??:h') . '/'
else
let default_root = s:Tree(dir) . '/'
endif
if a:destination =~# '^:/:\='
let destination = s:Tree(dir) . s:Expand(substitute(a:destination, '^:/:\=', '', ''))
elseif a:destination =~# '^:(top)'
let destination = s:Expand(matchstr(a:destination, ')\zs.*'))
if destination !~# '^/\|^\a\+:'
let destination = s:Tree(dir) . '/' . destination
endif
let destination = s:Tree(dir) .
elseif a:destination =~# '^:(\%(top,literal\|literal,top\))'
2019-11-16 10:28:42 -05:00
let destination = s:Tree(dir) . matchstr(a:destination, ')\zs.*')
2021-10-11 05:30:43 -04:00
elseif a:destination =~# '^:(literal)\.\.\=\%(/\|$\)'
2018-08-25 12:13:42 -04:00
let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
2021-10-11 05:30:43 -04:00
elseif a:destination =~# '^:(literal)'
let destination = simplify(default_root . matchstr(a:destination, ')\zs.*'))
2018-06-14 06:31:12 -04:00
else
2021-10-11 05:30:43 -04:00
let destination = s:Expand(a:destination)
if destination =~# '^\.\.\=\%(/\|$\)'
let destination = simplify(getcwd() . '/' . destination)
elseif destination !~# '^\a\+:\|^/'
let destination = default_root . destination
endif
2018-06-14 06:31:12 -04:00
endif
2018-08-25 12:13:42 -04:00
let destination = s:Slash(destination)
2018-07-30 17:18:16 -04:00
if isdirectory(@%)
setlocal noswapfile
2018-06-14 06:31:12 -04:00
endif
2021-10-11 05:30:43 -04:00
let exec = fugitive#Execute(['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination], dir)
if exec.exit_status && exec.stderr !=# ['']
return 'echoerr ' .string('fugitive: '.s:JoinChomp(exec.stderr))
2018-06-14 06:31:12 -04:00
endif
if isdirectory(destination)
let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
endif
2021-10-11 05:30:43 -04:00
let reload = '|call fugitive#DidChange(' . string(exec) . ')'
2018-07-30 17:18:16 -04:00
if empty(s:DirCommitFile(@%)[1])
2018-06-14 06:31:12 -04:00
if isdirectory(destination)
2019-11-16 10:28:42 -05:00
return 'keepalt edit '.s:fnameescape(destination) . reload
2018-06-14 06:31:12 -04:00
else
2019-11-16 10:28:42 -05:00
return 'keepalt saveas! '.s:fnameescape(destination) . reload
2018-06-14 06:31:12 -04:00
endif
else
2019-11-16 10:28:42 -05:00
return 'file '.s:fnameescape(fugitive#Find(':0:'.destination, dir)) . reload
2018-06-14 06:31:12 -04:00
endif
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#RenameComplete(A,L,P) abort
2018-08-25 12:13:42 -04:00
if a:A =~# '^[.:]\=/'
2019-05-17 10:09:13 -04:00
return fugitive#CompletePath(a:A)
2018-06-14 06:31:12 -04:00
else
2018-08-25 12:13:42 -04:00
let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
2019-05-17 10:09:13 -04:00
return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
2018-06-14 06:31:12 -04:00
endif
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#MoveCommand(line1, line2, range, bang, mods, arg, ...) abort
2019-11-16 10:28:42 -05:00
return s:Move(a:bang, 0, a:arg)
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#RenameCommand(line1, line2, range, bang, mods, arg, ...) abort
2019-11-16 10:28:42 -05:00
return s:Move(a:bang, 1, a:arg)
endfunction
2018-06-14 06:31:12 -04:00
function! s:Remove(after, force) abort
2019-11-16 10:28:42 -05:00
let dir = s:Dir()
exe s:DirCheck(dir)
if len(@%) && s:DirCommitFile(@%)[1] ==# ''
2018-06-14 06:31:12 -04:00
let cmd = ['rm']
2018-07-30 17:18:16 -04:00
elseif s:DirCommitFile(@%)[1] ==# '0'
2018-06-14 06:31:12 -04:00
let cmd = ['rm','--cached']
else
2019-11-16 10:28:42 -05:00
return 'echoerr ' . string('fugitive: rm not supported for this buffer')
2018-06-14 06:31:12 -04:00
endif
if a:force
let cmd += ['--force']
endif
2021-10-11 05:30:43 -04:00
let message = s:ChompStderr(cmd + ['--', expand('%:p')], dir)
if len(message)
2018-06-14 06:31:12 -04:00
let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
return 'echoerr '.string(v:errmsg)
else
2021-10-11 05:30:43 -04:00
return a:after . (a:force ? '!' : ''). '|call fugitive#DidChange(' . string(dir) . ')'
2018-06-14 06:31:12 -04:00
endif
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#RemoveCommand(line1, line2, range, bang, mods, arg, ...) abort
2019-11-16 10:28:42 -05:00
return s:Remove('edit', a:bang)
endfunction
2022-08-08 09:45:56 -04:00
function! fugitive#UnlinkCommand(line1, line2, range, bang, mods, arg, ...) abort
return s:Remove('edit', a:bang)
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#DeleteCommand(line1, line2, range, bang, mods, arg, ...) abort
2019-11-16 10:28:42 -05:00
return s:Remove('bdelete', a:bang)
endfunction
2018-06-14 06:31:12 -04:00
2020-04-25 21:56:16 -04:00
" Section: :Git blame
2018-06-14 06:31:12 -04:00
2018-07-30 17:18:16 -04:00
function! s:Keywordprg() abort
2022-08-08 09:45:56 -04:00
let args = ' --git-dir=' . escape(FugitiveGitPath(s:GitDir()), "\\\"' ")
2018-07-30 17:18:16 -04:00
if has('gui_running') && !has('win32')
2021-05-31 16:30:26 -04:00
return s:GitShellCmd() . ' --no-pager' . args . ' log -1'
2018-07-30 17:18:16 -04:00
else
2021-05-31 16:30:26 -04:00
return s:GitShellCmd() . args . ' show'
2018-07-30 17:18:16 -04:00
endif
endfunction
2018-06-14 06:31:12 -04:00
function! s:linechars(pattern) abort
let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
2022-08-08 09:45:56 -04:00
if &conceallevel > 1
2018-06-14 06:31:12 -04:00
for col in range(1, chars)
let chars -= synconcealed(line('.'), col)[0]
endfor
endif
return chars
endfunction
2019-08-22 11:36:17 -04:00
function! s:BlameBufnr(...) abort
2022-08-08 09:45:56 -04:00
let state = s:TempState(a:0 ? a:1 : bufnr(''))
2019-08-22 11:36:17 -04:00
if get(state, 'filetype', '') ==# 'fugitiveblame'
2021-05-05 04:25:00 -04:00
return get(state, 'origin_bufnr', -1)
2019-08-22 11:36:17 -04:00
else
return -1
endif
endfunction
function! s:BlameCommitFileLnum(...) abort
let line = a:0 ? a:1 : getline('.')
2021-05-05 04:25:00 -04:00
let state = a:0 > 1 ? a:2 : s:TempState()
if get(state, 'filetype', '') !=# 'fugitiveblame'
return ['', '', 0]
endif
2020-12-04 16:15:32 -05:00
let commit = matchstr(line, '^\^\=[?*]*\zs\x\+')
2019-08-22 11:36:17 -04:00
if commit =~# '^0\+$'
let commit = ''
2019-11-16 10:28:42 -05:00
elseif has_key(state, 'blame_reverse_end')
2021-10-11 05:30:43 -04:00
let commit = get(s:LinesError([state.git_dir, 'rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end])[0], 0, '')
2019-08-22 11:36:17 -04:00
endif
let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
2019-12-01 14:18:45 -05:00
let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s*\d\+ \%((\| *\d\+)\)')
2019-08-22 11:36:17 -04:00
if empty(path) && lnum
let path = get(state, 'blame_file', '')
endif
return [commit, path, lnum]
endfunction
function! s:BlameLeave() abort
let bufwinnr = bufwinnr(s:BlameBufnr())
if bufwinnr > 0
let bufnr = bufnr('')
exe bufwinnr . 'wincmd w'
return bufnr . 'bdelete'
endif
return ''
endfunction
function! s:BlameQuit() abort
let cmd = s:BlameLeave()
if empty(cmd)
2018-06-14 06:31:12 -04:00
return 'bdelete'
2019-08-22 11:36:17 -04:00
elseif len(s:DirCommitFile(@%)[1])
return cmd . '|Gedit'
else
return cmd
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
endfunction
2019-11-16 10:28:42 -05:00
function! fugitive#BlameComplete(A, L, P) abort
2019-08-22 11:36:17 -04:00
return s:CompleteSub('blame', a:A, a:L, a:P)
endfunction
2020-04-25 21:56:16 -04:00
function! s:BlameSubcommand(line1, count, range, bang, mods, options) abort
2021-10-11 05:30:43 -04:00
let dir = s:Dir(a:options)
2020-04-25 21:56:16 -04:00
exe s:DirCheck(dir)
2021-05-05 04:25:00 -04:00
let flags = copy(a:options.subcommand_args)
2019-08-22 11:36:17 -04:00
let i = 0
let raw = 0
let commits = []
let files = []
let ranges = []
if a:line1 > 0 && a:count > 0 && a:range != 1
call extend(ranges, ['-L', a:line1 . ',' . a:count])
endif
while i < len(flags)
let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
if len(match) && len(match[2])
call insert(flags, match[1])
let flags[i+1] = '-' . match[2]
continue
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
let arg = flags[i]
if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$'
let raw = 1
elseif arg ==# '--contents' && i + 1 < len(flags)
call extend(commits, remove(flags, i, i+1))
continue
elseif arg ==# '-L' && i + 1 < len(flags)
call extend(ranges, remove(flags, i, i+1))
continue
elseif arg =~# '^--contents='
call add(commits, remove(flags, i))
continue
elseif arg =~# '^-L.'
call add(ranges, remove(flags, i))
continue
2019-11-16 10:28:42 -05:00
elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\|ignore-rev\|ignore-revs-file\)$'
2019-08-22 11:36:17 -04:00
let i += 1
if i == len(flags)
echohl ErrorMsg
2021-10-11 05:30:43 -04:00
echo s:ChompStderr([dir, 'blame', arg])
2019-08-22 11:36:17 -04:00
echohl NONE
return ''
endif
elseif arg ==# '--'
if i + 1 < len(flags)
call extend(files, remove(flags, i + 1, -1))
endif
call remove(flags, i)
break
elseif arg !~# '^-' && (s:HasOpt(flags, '--not') || arg !~# '^\^')
if index(flags, '--') >= 0
call add(commits, remove(flags, i))
continue
endif
if arg =~# '\.\.' && arg !~# '^\.\.\=\%(/\|$\)' && empty(commits)
call add(commits, remove(flags, i))
continue
endif
try
2020-04-25 21:56:16 -04:00
let dcf = s:DirCommitFile(fugitive#Find(arg, dir))
2019-08-22 11:36:17 -04:00
if len(dcf[1]) && empty(dcf[2])
call add(commits, remove(flags, i))
continue
endif
catch /^fugitive:/
endtry
call add(files, remove(flags, i))
continue
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
let i += 1
endwhile
2020-04-25 21:56:16 -04:00
let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./', dir))), '^\.\%(/\|$\)', '', '')
2019-08-22 11:36:17 -04:00
if empty(commits) && len(files) > 1
call add(commits, remove(files, 1))
endif
2019-11-16 10:28:42 -05:00
exe s:BlameLeave()
2019-08-22 11:36:17 -04:00
try
2021-05-05 04:25:00 -04:00
let cmd = a:options.flags + ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', a:options.subcommand, '--show-number']
2019-08-22 11:36:17 -04:00
call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
if a:count > 0 && empty(ranges)
let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
endif
call extend(cmd, ranges)
2021-05-05 04:25:00 -04:00
let tempname = tempname()
let temp = tempname . (raw ? '' : '.fugitiveblame')
2019-08-22 11:36:17 -04:00
if len(commits)
let cmd += commits
elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$'))
let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')]
elseif empty(files) && !s:HasOpt(flags, '--reverse')
2022-08-08 09:45:56 -04:00
if &modified || !empty(s:DirCommitFile(@%)[1])
let cmd += ['--contents', tempname . '.in']
silent execute 'noautocmd keepalt %write ' . s:fnameescape(tempname . '.in')
let delete_in = 1
elseif &autoread
exe 'checktime ' . bufnr('')
endif
2021-10-11 05:30:43 -04:00
else
call fugitive#Autowrite()
2018-07-30 17:18:16 -04:00
endif
2022-08-08 09:45:56 -04:00
let basecmd = [{'git': a:options.git}, dir, '--literal-pathspecs'] + cmd + ['--'] + (len(files) ? files : [file])
2021-10-11 05:30:43 -04:00
let [err, exec_error] = s:StdoutToFile(temp, basecmd)
2021-05-05 04:25:00 -04:00
if exists('delete_in')
call delete(tempname . '.in')
2018-06-14 06:31:12 -04:00
endif
2019-08-22 11:36:17 -04:00
redraw
2019-03-08 06:04:56 -05:00
try
2021-05-05 04:25:00 -04:00
if exec_error
let lines = split(err, "\n")
2019-08-22 11:36:17 -04:00
if empty(lines)
let lines = readfile(temp)
endif
for i in range(len(lines))
if lines[i] =~# '^error: \|^fatal: '
echohl ErrorMsg
echon lines[i]
echohl NONE
break
else
echon lines[i]
endif
if i != len(lines) - 1
echon "\n"
endif
endfor
return ''
endif
2021-05-05 04:25:00 -04:00
let temp_state = {
\ 'git': a:options.git,
\ 'flags': a:options.flags,
\ 'args': [a:options.subcommand] + a:options.subcommand_args,
2022-08-08 09:45:56 -04:00
\ 'git_dir': s:GitDir(a:options),
\ 'cwd': s:UserCommandCwd(a:options),
2021-05-05 04:25:00 -04:00
\ 'filetype': (raw ? 'git' : 'fugitiveblame'),
\ 'blame_options': a:options,
\ 'blame_flags': flags,
\ 'blame_file': file}
2019-08-22 11:36:17 -04:00
if s:HasOpt(flags, '--reverse')
let temp_state.blame_reverse_end = matchstr(get(commits, 0, ''), '\.\.\zs.*')
2018-06-14 06:31:12 -04:00
endif
2021-10-11 05:30:43 -04:00
if a:line1 == 0 && a:count == 1
if get(a:options, 'curwin')
let edit = 'edit'
elseif a:bang
let edit = 'pedit'
else
let edit = 'split'
endif
return s:BlameCommit(s:Mods(a:mods) . edit, get(readfile(temp), 0, ''), temp_state)
elseif (a:line1 == 0 || a:range == 1) && a:count > 0
let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit', 'edit'], a:count - (a:line1 ? a:line1 : 1), 'split')
2019-08-22 11:36:17 -04:00
return s:BlameCommit(edit, get(readfile(temp), 0, ''), temp_state)
2018-06-14 06:31:12 -04:00
else
2019-08-22 11:36:17 -04:00
let temp = s:Resolve(temp)
2021-05-05 04:25:00 -04:00
let temp_state.file = temp
call s:RunSave(temp_state)
2019-08-22 11:36:17 -04:00
if len(ranges + commits + files) || raw
2021-10-11 05:30:43 -04:00
let reload = '|call fugitive#DidChange(fugitive#Result(' . string(temp_state.file) . '))'
2019-08-22 11:36:17 -04:00
let mods = s:Mods(a:mods)
if a:count != 0
2021-10-11 05:30:43 -04:00
exe 'silent keepalt' mods get(a:options, 'curwin') ? 'edit' : 'split' s:fnameescape(temp)
2019-08-22 11:36:17 -04:00
elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden)
exe 'silent' mods 'edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp)
else
2021-05-05 04:25:00 -04:00
return mods . 'edit ' . s:fnameescape(temp) . reload
2019-08-22 11:36:17 -04:00
endif
2021-05-05 04:25:00 -04:00
return reload[1 : -1]
2019-08-22 11:36:17 -04:00
endif
if a:mods =~# '\<tab\>'
silent tabedit %
endif
2020-04-25 21:56:16 -04:00
let bufnr = bufnr('')
2021-05-05 04:25:00 -04:00
let temp_state.origin_bufnr = bufnr
2020-04-25 21:56:16 -04:00
let restore = []
2019-08-22 11:36:17 -04:00
let mods = substitute(a:mods, '\<tab\>', '', 'g')
2018-06-14 06:31:12 -04:00
for winnr in range(winnr('$'),1,-1)
2019-08-22 11:36:17 -04:00
if getwinvar(winnr, '&scrollbind')
2020-04-25 21:56:16 -04:00
if !&l:scrollbind
call setwinvar(winnr, '&scrollbind', 0)
elseif winnr != winnr() && getwinvar(winnr, '&foldenable')
call setwinvar(winnr, '&foldenable', 0)
call add(restore, 'call setwinvar(bufwinnr('.winbufnr(winnr).'),"&foldenable",1)')
endif
2019-08-22 11:36:17 -04:00
endif
2022-08-08 09:45:56 -04:00
let win_blame_bufnr = s:BlameBufnr(winbufnr(winnr))
if getwinvar(winnr, '&scrollbind') ? win_blame_bufnr == bufnr : win_blame_bufnr > 0
2018-06-14 06:31:12 -04:00
execute winbufnr(winnr).'bdelete'
endif
endfor
2022-08-08 09:45:56 -04:00
let restore_winnr = exists('*win_getid') ? win_getid() : 'bufwinnr(' . bufnr . ')'
2020-04-25 21:56:16 -04:00
if !&l:scrollbind
call add(restore, 'call setwinvar(' . restore_winnr . ',"&scrollbind",0)')
endif
2018-06-14 06:31:12 -04:00
if &l:wrap
2020-04-25 21:56:16 -04:00
call add(restore, 'call setwinvar(' . restore_winnr . ',"&wrap",1)')
2018-06-14 06:31:12 -04:00
endif
if &l:foldenable
2020-04-25 21:56:16 -04:00
call add(restore, 'call setwinvar(' . restore_winnr . ',"&foldenable",1)')
2018-06-14 06:31:12 -04:00
endif
setlocal scrollbind nowrap nofoldenable
let top = line('w0') + &scrolloff
let current = line('.')
2019-08-22 11:36:17 -04:00
exe 'silent keepalt' (a:bang ? s:Mods(mods) . 'split' : s:Mods(mods, 'leftabove') . 'vsplit') s:fnameescape(temp)
2020-04-25 21:56:16 -04:00
let w:fugitive_leave = join(restore, '|')
2018-06-14 06:31:12 -04:00
execute top
normal! zt
execute current
2019-08-22 11:36:17 -04:00
setlocal nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth
2018-06-14 06:31:12 -04:00
if exists('+relativenumber')
setlocal norelativenumber
endif
2019-11-16 10:28:42 -05:00
if exists('+signcolumn')
setlocal signcolumn=no
endif
2021-05-05 04:25:00 -04:00
execute "vertical resize ".(s:linechars('.\{-\}\s\+\d\+\ze)')+1)
2018-06-14 06:31:12 -04:00
redraw
syncbind
2021-05-05 04:25:00 -04:00
exe s:DoAutocmdChanged(temp_state)
2018-06-14 06:31:12 -04:00
endif
endtry
return ''
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-06-14 06:31:12 -04:00
endtry
endfunction
2018-07-30 17:18:16 -04:00
function! s:BlameCommit(cmd, ...) abort
let line = a:0 ? a:1 : getline('.')
2019-08-22 11:36:17 -04:00
let state = a:0 ? a:2 : s:TempState()
let sigil = has_key(state, 'blame_reverse_end') ? '-' : '+'
let mods = (s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ")
let [commit, path, lnum] = s:BlameCommitFileLnum(line, state)
if empty(commit) && len(path) && has_key(state, 'blame_reverse_end')
let path = (len(state.blame_reverse_end) ? state.blame_reverse_end . ':' : ':(top)') . path
2019-11-16 10:28:42 -05:00
return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
2019-08-22 11:36:17 -04:00
endif
if commit =~# '^0*$'
return 'echoerr ' . string('fugitive: no commit')
endif
if line =~# '^\^' && !has_key(state, 'blame_reverse_end')
let path = commit . ':' . path
2019-11-16 10:28:42 -05:00
return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
2019-08-22 11:36:17 -04:00
endif
2019-11-16 10:28:42 -05:00
let cmd = fugitive#Open(mods . a:cmd, 0, '', commit, [commit])
2018-06-14 06:31:12 -04:00
if cmd =~# '^echoerr'
return cmd
endif
execute cmd
2019-08-22 11:36:17 -04:00
if a:cmd ==# 'pedit' || empty(path)
2018-07-30 17:18:16 -04:00
return ''
endif
2018-06-14 06:31:12 -04:00
if search('^diff .* b/\M'.escape(path,'\').'$','W')
call search('^+++')
let head = line('.')
while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
2019-08-22 11:36:17 -04:00
let top = +matchstr(getline('.'),' ' . sigil .'\zs\d\+')
let len = +matchstr(getline('.'),' ' . sigil . '\d\+,\zs\d\+')
2018-06-14 06:31:12 -04:00
if lnum >= top && lnum <= top + len
let offset = lnum - top
if &scrolloff
+
normal! zt
else
normal! zt
+
endif
while offset > 0 && line('.') < line('$')
+
2019-08-22 11:36:17 -04:00
if getline('.') =~# '^[ ' . sigil . ']'
2018-06-14 06:31:12 -04:00
let offset -= 1
endif
endwhile
return 'normal! zv'
endif
endwhile
execute head
normal! zt
endif
return ''
endfunction
2019-08-22 11:36:17 -04:00
function! s:BlameJump(suffix, ...) abort
2019-03-08 06:04:56 -05:00
let suffix = a:suffix
2019-08-22 11:36:17 -04:00
let [commit, path, lnum] = s:BlameCommitFileLnum()
if empty(path)
return 'echoerr ' . string('fugitive: could not determine filename for blame')
endif
if commit =~# '^0*$'
2021-05-05 04:25:00 -04:00
let commit = '@'
2019-03-08 06:04:56 -05:00
let suffix = ''
2018-06-14 06:31:12 -04:00
endif
let offset = line('.') - line('w0')
2020-04-25 21:56:16 -04:00
let state = s:TempState()
let flags = get(state, 'blame_flags', [])
2019-08-22 11:36:17 -04:00
let blame_bufnr = s:BlameBufnr()
if blame_bufnr > 0
let bufnr = bufnr('')
let winnr = bufwinnr(blame_bufnr)
if winnr > 0
exe winnr.'wincmd w'
2019-11-30 07:06:56 -05:00
exe bufnr.'bdelete'
2019-08-22 11:36:17 -04:00
endif
execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
execute lnum
2018-06-14 06:31:12 -04:00
endif
2020-04-25 21:56:16 -04:00
let my_bufnr = bufnr('')
if blame_bufnr < 0
let blame_args = flags + [commit . suffix, '--', path]
2021-05-05 04:25:00 -04:00
let result = s:BlameSubcommand(0, 0, 0, 0, '', extend({'subcommand_args': blame_args}, state.blame_options, 'keep'))
2020-04-25 21:56:16 -04:00
else
let blame_args = flags
2021-05-05 04:25:00 -04:00
let result = s:BlameSubcommand(-1, -1, 0, 0, '', extend({'subcommand_args': blame_args}, state.blame_options, 'keep'))
2020-04-25 21:56:16 -04:00
endif
if bufnr('') == my_bufnr
return result
endif
execute result
execute lnum
let delta = line('.') - line('w0') - offset
if delta > 0
execute 'normal! '.delta."\<C-E>"
elseif delta < 0
execute 'normal! '.(-delta)."\<C-Y>"
2018-06-14 06:31:12 -04:00
endif
2020-04-25 21:56:16 -04:00
keepjumps syncbind
redraw
echo ':Git blame' s:fnameescape(blame_args)
2018-06-14 06:31:12 -04:00
return ''
endfunction
let s:hash_colors = {}
2019-08-22 11:36:17 -04:00
function! fugitive#BlameSyntax() abort
2018-06-14 06:31:12 -04:00
let conceal = has('conceal') ? ' conceal' : ''
2019-08-22 11:36:17 -04:00
let flags = get(s:TempState(), 'blame_flags', [])
2020-12-04 16:15:32 -05:00
syn spell notoplevel
2019-08-22 11:36:17 -04:00
syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
2019-11-16 10:28:42 -05:00
syn match FugitiveblameHash "\%(^\^\=[?*]*\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
2020-04-25 21:56:16 -04:00
if s:HasOpt(flags, '-b') || FugitiveConfigGet('blame.blankBoundary') =~# '^1$\|^true$'
2019-11-16 10:28:42 -05:00
syn match FugitiveblameBoundaryIgnore "^\^[*?]*\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
2019-08-22 11:36:17 -04:00
else
syn match FugitiveblameBoundary "^\^"
endif
syn match FugitiveblameScoreDebug " *\d\+\s\+\d\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile contained skipwhite
syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
2020-12-04 16:15:32 -05:00
syn match FugitiveblameTime "\<[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
2021-05-05 04:25:00 -04:00
exec 'syn match FugitiveblameLineNumber "\s[[:digit:][:space:]]\{0,' . (len(line('$'))-1). '\}\d)\@=" contained containedin=FugitiveblameAnnotation' conceal
2019-08-22 11:36:17 -04:00
exec 'syn match FugitiveblameOriginalFile "\s\%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-name', '-f') ? '' : conceal)
exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
2018-06-14 06:31:12 -04:00
syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
hi def link FugitiveblameBoundary Keyword
hi def link FugitiveblameHash Identifier
2019-08-22 11:36:17 -04:00
hi def link FugitiveblameBoundaryIgnore Ignore
2018-06-14 06:31:12 -04:00
hi def link FugitiveblameUncommitted Ignore
2019-08-22 11:36:17 -04:00
hi def link FugitiveblameScoreDebug Debug
2018-06-14 06:31:12 -04:00
hi def link FugitiveblameTime PreProc
hi def link FugitiveblameLineNumber Number
hi def link FugitiveblameOriginalFile String
hi def link FugitiveblameOriginalLineNumber Float
hi def link FugitiveblameShort FugitiveblameDelimiter
hi def link FugitiveblameDelimiter Delimiter
hi def link FugitiveblameNotCommittedYet Comment
2019-08-22 11:36:17 -04:00
if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
return
endif
2018-06-14 06:31:12 -04:00
let seen = {}
for lnum in range(1, line('$'))
2021-05-05 04:25:00 -04:00
let orig_hash = matchstr(getline(lnum), '^\^\=[*?]*\zs\x\{6\}')
let hash = orig_hash
let hash = substitute(hash, '\(\x\)\x', '\=submatch(1).printf("%x", 15-str2nr(submatch(1),16))', 'g')
let hash = substitute(hash, '\(\x\x\)', '\=printf("%02x", str2nr(submatch(1),16)*3/4+32)', 'g')
if hash ==# '' || orig_hash ==# '000000' || has_key(seen, hash)
2018-06-14 06:31:12 -04:00
continue
endif
let seen[hash] = 1
2021-05-05 04:25:00 -04:00
if &t_Co == 256
let [s, r, g, b; __] = map(matchlist(orig_hash, '\(\x\)\x\(\x\)\x\(\x\)\x'), 'str2nr(v:val,16)')
let color = 16 + (r + 1) / 3 * 36 + (g + 1) / 3 * 6 + (b + 1) / 3
if color == 16
let color = 235
elseif color == 231
let color = 255
2018-06-14 06:31:12 -04:00
endif
let s:hash_colors[hash] = ' ctermfg='.color
else
let s:hash_colors[hash] = ''
endif
2021-05-05 04:25:00 -04:00
let pattern = substitute(orig_hash, '^\(\x\)\x\(\x\)\x\(\x\)\x$', '\1\\x\2\\x\3\\x', '') . '*\>'
exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=[*?]*\)\@<='.pattern.'" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
2018-06-14 06:31:12 -04:00
endfor
2021-05-05 04:25:00 -04:00
syn match FugitiveblameUncommitted "\%(^\^\=[?*]*\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
2019-08-22 11:36:17 -04:00
call s:BlameRehighlight()
2018-06-14 06:31:12 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:BlameRehighlight() abort
2018-06-14 06:31:12 -04:00
for [hash, cterm] in items(s:hash_colors)
if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
2021-05-05 04:25:00 -04:00
exe 'hi FugitiveblameHash'.hash.' guifg=#' . hash . cterm
2018-06-14 06:31:12 -04:00
else
exe 'hi link FugitiveblameHash'.hash.' Identifier'
endif
endfor
endfunction
2021-10-28 15:48:21 -04:00
function! s:BlameMaps(is_ftplugin) abort
let ft = a:is_ftplugin
call s:Map('n', '<F1>', ':help :Git_blame<CR>', '<silent>', ft)
call s:Map('n', 'g?', ':help :Git_blame<CR>', '<silent>', ft)
call s:Map('n', 'gq', ':exe <SID>BlameQuit()<CR>', '<silent>', ft)
call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>', ft)
call s:Map('n', '<CR>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>', ft)
call s:Map('n', '-', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>', ft)
call s:Map('n', 's', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>', ft)
call s:Map('n', 'u', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>', ft)
call s:Map('n', 'P', ':<C-U>exe <SID>BlameJump("^".v:count1)<CR>', '<silent>', ft)
call s:Map('n', '~', ':<C-U>exe <SID>BlameJump("~".v:count1)<CR>', '<silent>', ft)
call s:Map('n', 'i', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>', ft)
call s:Map('n', 'o', ':<C-U>exe <SID>BlameCommit("split")<CR>', '<silent>', ft)
call s:Map('n', 'O', ':<C-U>exe <SID>BlameCommit("tabedit")<CR>', '<silent>', ft)
call s:Map('n', 'p', ':<C-U>exe <SID>BlameCommit("pedit")<CR>', '<silent>', ft)
exe s:Map('n', '.', ":<C-U> <C-R>=substitute(<SID>BlameCommitFileLnum()[0],'^$','@','')<CR><Home>", '', ft)
exe s:Map('n', '(', "-", '', ft)
exe s:Map('n', ')', "+", '', ft)
2021-10-28 15:48:21 -04:00
call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>', ft)
call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>', ft)
call s:Map('n', 'D', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze\\d\\ze\\s\\+\\d\\+)')+1-v:count)<CR>", '<silent>', ft)
endfunction
2021-05-05 04:25:00 -04:00
function! fugitive#BlameFileType() abort
2019-08-22 11:36:17 -04:00
setlocal nomodeline
setlocal foldmethod=manual
2022-08-08 09:45:56 -04:00
if len(s:GitDir())
2019-08-22 11:36:17 -04:00
let &l:keywordprg = s:Keywordprg()
endif
let b:undo_ftplugin = 'setl keywordprg= foldmethod<'
if exists('+concealcursor')
setlocal concealcursor=nc conceallevel=2
let b:undo_ftplugin .= ' concealcursor< conceallevel<'
endif
if &modifiable
return ''
endif
2021-10-28 15:48:21 -04:00
call s:BlameMaps(1)
2019-08-22 11:36:17 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:BlameCursorSync(bufnr, line) abort
if a:line == line('.')
return
endif
if get(s:TempState(), 'origin_bufnr') == a:bufnr || get(s:TempState(a:bufnr), 'origin_bufnr') == bufnr('')
if &startofline
execute a:line
else
let pos = getpos('.')
let pos[1] = a:line
call setpos('.', pos)
endif
endif
endfunction
2019-08-22 11:36:17 -04:00
augroup fugitive_blame
autocmd!
autocmd ColorScheme,GUIEnter * call s:BlameRehighlight()
autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
2022-08-08 09:45:56 -04:00
autocmd WinLeave * let s:cursor_for_blame = [bufnr(''), line('.')]
autocmd WinEnter * if exists('s:cursor_for_blame') | call call('s:BlameCursorSync', s:cursor_for_blame) | endif
2019-08-22 11:36:17 -04:00
augroup END
2021-05-05 04:25:00 -04:00
" Section: :GBrowse
2018-06-14 06:31:12 -04:00
2021-05-05 04:25:00 -04:00
function! s:BrowserOpen(url, mods, echo_copy) abort
2022-08-08 09:45:56 -04:00
let [_, main, query, anchor; __] = matchlist(a:url, '^\([^#?]*\)\(?[^#]*\)\=\(#.*\)\=')
let url = main . tr(query, ' ', '+') . anchor
let url = substitute(url, '[ <>\|"]', '\="%".printf("%02X",char2nr(submatch(0)))', 'g')
2021-05-05 04:25:00 -04:00
let mods = s:Mods(a:mods)
if a:echo_copy
if has('clipboard')
let @+ = url
endif
return 'echo '.string(url)
elseif exists(':Browse') == 2
return 'echo '.string(url).'|' . mods . 'Browse '.url
elseif exists(':OpenBrowser') == 2
return 'echo '.string(url).'|' . mods . 'OpenBrowser '.url
else
if !exists('g:loaded_netrw')
runtime! autoload/netrw.vim
endif
if exists('*netrw#BrowseX')
return 'echo '.string(url).'|' . mods . 'call netrw#BrowseX('.string(url).', 0)'
elseif exists('*netrw#NetrwBrowseX')
return 'echo '.string(url).'|' . mods . 'call netrw#NetrwBrowseX('.string(url).', 0)'
else
return 'echoerr ' . string('Netrw not found. Define your own :Browse to use :GBrowse')
endif
endif
endfunction
2018-06-14 06:31:12 -04:00
2021-10-11 05:30:43 -04:00
function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, ...) abort
2021-05-05 04:25:00 -04:00
exe s:VersionCheck()
2019-03-08 06:04:56 -05:00
let dir = s:Dir()
2018-06-14 06:31:12 -04:00
try
2021-05-05 04:25:00 -04:00
let arg = a:arg
2021-10-11 05:30:43 -04:00
if arg =~# '^++\%([Gg]it\)\=[Rr]emote='
let remote = matchstr(arg, '^++\%([Gg]it\)\=[Rr]emote=\zs\S\+')
2021-05-05 04:25:00 -04:00
let arg = matchstr(arg, '\s\zs\S.*')
endif
2022-08-08 09:45:56 -04:00
let validremote = '\.\%(git\)\=\|\.\=/.*\|\a[[:alnum:]_-]*\%(://.\{-\}\)\='
2021-05-05 04:25:00 -04:00
if arg ==# '-'
let remote = ''
let rev = ''
let result = fugitive#Result()
if filereadable(get(result, 'file', ''))
2021-07-30 16:52:54 -04:00
let rev = s:fnameescape(result.file)
else
return 'echoerr ' . string('fugitive: could not find prior :Git invocation')
2019-08-22 11:36:17 -04:00
endif
2021-05-05 04:25:00 -04:00
elseif !exists('l:remote')
2022-08-08 09:45:56 -04:00
let remote = matchstr(arg, '\\\@<!\%(\\\\\)*[!@]\zs\%('.validremote.'\)$')
let rev = strpart(arg, 0, len(arg) - len(remote) - (empty(remote) ? 0 : 1))
2018-06-14 06:31:12 -04:00
else
2021-05-05 04:25:00 -04:00
let rev = arg
endif
let expanded = s:Expand(rev)
if expanded =~? '^\a\a\+:[\/][\/]' && expanded !~? '^fugitive:'
return s:BrowserOpen(s:Slash(expanded), a:mods, a:bang)
2018-08-25 12:13:42 -04:00
endif
2021-07-30 16:52:54 -04:00
if !exists('l:result')
2022-08-08 09:45:56 -04:00
let result = s:TempState(empty(expanded) ? bufnr('') : expanded)
2021-07-30 16:52:54 -04:00
endif
2022-08-08 09:45:56 -04:00
if !get(result, 'origin_bufnr', 1) && filereadable(get(result, 'file', ''))
2021-07-30 16:52:54 -04:00
for line in readfile(result.file, '', 4096)
let rev = s:fnameescape(matchstr(line, '\<https\=://[^[:space:]<>]*[^[:space:]<>.,;:"''!?]'))
if len(rev)
2021-10-11 05:30:43 -04:00
return s:BrowserOpen(rev, a:mods, a:bang)
2021-07-30 16:52:54 -04:00
endif
endfor
2021-10-11 05:30:43 -04:00
return 'echoerr ' . string('fugitive: no URL found in output of :Git')
2021-07-30 16:52:54 -04:00
endif
2021-05-05 04:25:00 -04:00
exe s:DirCheck(dir)
2022-08-08 09:45:56 -04:00
let config = fugitive#Config(dir)
if empty(remote) && expanded =~# '^[^-./:^~][^:^~]*$' && !empty(FugitiveConfigGet('remote.' . expanded . '.url', config))
let remote = expanded
let expanded = ''
endif
2021-05-05 04:25:00 -04:00
if empty(expanded)
2022-08-08 09:45:56 -04:00
let bufname = &buftype =~# '^\%(nofile\|terminal\)$' ? '' : s:BufName('%')
2021-05-05 04:25:00 -04:00
let expanded = s:DirRev(bufname)[1]
if empty(expanded)
let expanded = fugitive#Path(bufname, ':(top)', dir)
endif
2022-08-08 09:45:56 -04:00
if a:count > 0 && has_key(result, 'origin_bufnr') && a:range != 2
2021-05-05 04:25:00 -04:00
let blame = s:BlameCommitFileLnum(getline(a:count))
if len(blame[0])
let expanded = blame[0]
endif
endif
2018-06-14 06:31:12 -04:00
endif
2021-05-05 04:25:00 -04:00
let full = s:Generate(expanded, dir)
2018-06-14 06:31:12 -04:00
let commit = ''
2022-08-08 09:45:56 -04:00
let ref = ''
let forbid_ref_as_commit = 0
if full =~# '^fugitive:'
2021-05-05 04:25:00 -04:00
let [dir, commit, path] = s:DirCommitFile(full)
2022-08-08 09:45:56 -04:00
if commit =~# '^\d\=$'
2018-07-04 06:53:25 -04:00
let commit = ''
2022-08-08 09:45:56 -04:00
let type = path =~# '^/\=$' ? 'tree' : 'blob'
2018-06-14 06:31:12 -04:00
else
2022-08-08 09:45:56 -04:00
let ref_match = matchlist(expanded, '^\(@{\@!\|[^:~^@]\+\)\(:\%(//\)\@!\|[~^@]\|$\)')
let ref = get(ref_match, 1, '')
let forbid_ref_as_commit = ref =~# '^@\=$' || ref_match[2] !~# '^:\=$'
if empty(path) && !forbid_ref_as_commit
let type = 'ref'
else
let type = s:ChompDefault(empty(path) ? 'commit': 'blob',
\ ['cat-file', '-t', commit . substitute(path, '^/', ':', '')], dir)
endif
2018-06-14 06:31:12 -04:00
endif
let path = path[1:-1]
2019-03-08 06:04:56 -05:00
elseif empty(s:Tree(dir))
let path = '.git/' . full[strlen(dir)+1:-1]
2018-06-14 06:31:12 -04:00
let type = ''
else
2019-11-16 10:28:42 -05:00
let path = fugitive#Path(full, '/')[1:-1]
2022-08-08 09:45:56 -04:00
if empty(path) || isdirectory(full)
2018-06-14 06:31:12 -04:00
let type = 'tree'
else
let type = 'blob'
endif
endif
2022-08-08 09:45:56 -04:00
if path =~# '^\.git/'
let ref = matchstr(path, '^.git/\zs\%(refs/[^/]\+/[^/].*\|\w*HEAD\)$')
let type = empty(ref) ? 'root': 'ref'
let path = ''
endif
if empty(ref) || ref ==# 'HEAD' || ref ==# '@'
let ref = fugitive#Head(-1, dir)
endif
if ref =~# '^\x\{40,\}$'
let ref = ''
elseif !empty(ref) && ref !~# '^refs/'
let ref = FugitiveExecute(['rev-parse', '--symbolic-full-name', ref], dir).stdout[0]
if ref !~# '^refs/'
let ref = ''
2018-06-14 06:31:12 -04:00
endif
endif
let merge = ''
2022-08-08 09:45:56 -04:00
if !empty(remote) && ref =~# '^refs/remotes/[^/]\+/[^/]\|^refs/heads/[^/]'
let merge = matchstr(ref, '^refs/\%(heads/\|remotes/[^/]\+/\)\zs.\+')
let ref = 'refs/heads/' . merge
elseif ref =~# '^refs/remotes/[^/]\+/[^/]'
let remote = matchstr(ref, '^refs/remotes/\zs[^/]\+')
let merge = matchstr(ref, '^refs/remotes/[^/]\+/\zs.\+')
let ref = 'refs/heads/' . merge
elseif ref =~# '^refs/heads/[^/]'
let merge = strpart(ref, 11)
let r = FugitiveConfigGet('branch.' . merge . '.remote', config)
let m = FugitiveConfigGet('branch.' . merge . '.merge', config)[11:-1]
2018-06-14 06:31:12 -04:00
if r ==# '.' && !empty(m)
2021-10-11 05:30:43 -04:00
let r2 = FugitiveConfigGet('branch.'.m.'.remote', config)
2018-06-14 06:31:12 -04:00
if r2 !~# '^\.\=$'
let r = r2
2021-10-11 05:30:43 -04:00
let m = FugitiveConfigGet('branch.'.m.'.merge', config)[11:-1]
2018-06-14 06:31:12 -04:00
endif
endif
2022-08-08 09:45:56 -04:00
if r !~# '^\.\=$'
2018-06-14 06:31:12 -04:00
let remote = r
endif
2022-08-08 09:45:56 -04:00
if !empty(remote)
let remote_ref = 'refs/remotes/' . remote . '/' . merge
2021-10-11 05:30:43 -04:00
if FugitiveConfigGet('push.default', config) ==# 'upstream' ||
\ !filereadable(FugitiveFind('.git/' . remote_ref, dir)) && empty(s:ChompDefault('', ['rev-parse', '--verify', remote_ref, '--'], dir))
2021-05-31 16:30:26 -04:00
let merge = m
2022-08-08 09:45:56 -04:00
let ref = 'refs/heads/' . merge
2018-06-14 06:31:12 -04:00
endif
endif
endif
2022-08-08 09:45:56 -04:00
if empty(remote) || remote ==# '.'
let remote = s:RemoteDefault(config)
endif
if empty(merge) || empty(remote)
let provider_ref = ref
else
let provider_ref = 'refs/remotes/' . remote . '/' . merge
endif
if forbid_ref_as_commit || a:count >= 0
let ref = ''
if type ==# 'ref'
let type = 'commit'
endif
elseif type ==# 'ref' && ref =~# '^refs/\%(heads\|tags\)/[^/]'
let commit = matchstr(ref, '^\Crefs/\%(heads\|tags\)/\zs.*')
endif
2021-05-05 04:25:00 -04:00
let line1 = a:count > 0 && type ==# 'blob' ? a:line1 : 0
let line2 = a:count > 0 && type ==# 'blob' ? a:count : 0
2022-08-08 09:45:56 -04:00
if empty(commit) && type =~# '^\%(tree\|blob\)$'
if a:count < 0
let commit = matchstr(ref, '^\Crefs/\%(heads\|tags\)/\zs.*')
elseif len(provider_ref)
let owner = s:Owner(@%, dir)
let commit = s:ChompDefault('', ['merge-base', provider_ref, empty(owner) ? '@' : owner, '--'], dir)
if line2 > 0 && empty(arg) && commit =~# '^\x\{40,\}$' && type ==# 'blob'
let blame_list = tempname()
call writefile([commit, ''], blame_list, 'b')
let blame_cmd = ['-c', 'blame.coloring=none', 'blame', '-L', line1.','.line2, '-S', blame_list, '-s', '--show-number']
if !&l:modified || has_key(result, 'origin_bufnr')
let [blame, exec_error] = s:LinesError(blame_cmd + ['./' . path], dir)
else
2018-08-25 12:13:42 -04:00
let blame_in = tempname()
2021-07-30 16:52:54 -04:00
silent exe 'noautocmd keepalt %write' blame_in
2022-08-08 09:45:56 -04:00
let [blame, exec_error] = s:LinesError(blame_cmd + ['--contents', blame_in, './' . path], dir)
call delete(blame_in)
endif
call delete(blame_list)
if !exec_error
let blame_regex = '^\^\x\+\s\+\zs\d\+\ze\s'
if get(blame, 0) =~# blame_regex && get(blame, -1) =~# blame_regex
let line1 = +matchstr(blame[0], blame_regex)
let line2 = +matchstr(blame[-1], blame_regex)
else
throw "fugitive: can't browse to unpushed change"
2018-08-25 12:13:42 -04:00
endif
endif
endif
endif
if empty(commit)
2022-08-08 09:45:56 -04:00
let commit = fugitive#RevParse(empty(ref) ? 'HEAD' : ref, dir)
2018-06-14 06:31:12 -04:00
endif
endif
2021-10-11 05:30:43 -04:00
if remote =~# ':'
let remote_url = remote
else
let remote_url = fugitive#RemoteUrl(remote, config)
2018-06-14 06:31:12 -04:00
endif
2021-10-11 05:30:43 -04:00
let raw = empty(remote_url) ? remote : remote_url
let git_dir = s:GitDir(dir)
2018-06-14 06:31:12 -04:00
2018-07-30 17:18:16 -04:00
let opts = {
2021-10-11 05:30:43 -04:00
\ 'git_dir': git_dir,
\ 'repo': {'git_dir': git_dir},
2018-07-30 17:18:16 -04:00
\ 'remote': raw,
2021-10-11 05:30:43 -04:00
\ 'remote_name': remote,
2022-08-08 09:45:56 -04:00
\ 'commit': s:UrlEncode(commit),
\ 'path': substitute(s:UrlEncode(path), '%20', ' ', 'g'),
2018-07-30 17:18:16 -04:00
\ 'type': type,
2018-08-25 12:13:42 -04:00
\ 'line1': line1,
\ 'line2': line2}
2018-07-30 17:18:16 -04:00
2022-08-08 09:45:56 -04:00
if empty(path)
if type ==# 'ref' && ref =~# '^refs/'
let opts.path = '.git/' . s:UrlEncode(ref)
let opts.type = ''
elseif type ==# 'root'
let opts.path ='.git/index'
let opts.type = ''
2021-05-05 04:25:00 -04:00
endif
2022-08-08 09:45:56 -04:00
elseif type ==# 'tree' && !empty(path)
let opts.path = s:sub(opts.path, '/\=$', '/')
2018-06-14 06:31:12 -04:00
endif
2022-08-08 09:45:56 -04:00
for l:.Handler in get(g:, 'fugitive_browse_handlers', [])
let l:.url = call(Handler, [copy(opts)])
if type(url) == type('') && url =~# '://'
return s:BrowserOpen(url, a:mods, a:bang)
endif
endfor
throw "fugitive: no GBrowse handler installed for '".raw."'"
2018-06-14 06:31:12 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-06-14 06:31:12 -04:00
endtry
endfunction
2018-07-30 17:18:16 -04:00
" Section: Go to file
2018-06-14 06:31:12 -04:00
2020-04-25 21:56:16 -04:00
let s:ref_header = '\%(Merge\|Rebase\|Upstream\|Pull\|Push\)'
2018-07-30 17:18:16 -04:00
nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
function! fugitive#MapCfile(...) abort
exe 'cnoremap <buffer> <expr> <Plug><cfile>' (a:0 ? a:1 : 'fugitive#Cfile()')
let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap <buffer> <Plug><cfile>"'
if !exists('g:fugitive_no_maps')
2019-08-22 11:36:17 -04:00
call s:Map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>', 1)
call s:Map('n', '<C-W>f', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
call s:Map('n', '<C-W><C-F>', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
call s:Map('n', '<C-W>gf', '<SID>:tabfind <Plug><cfile><CR>', '<silent><unique>', 1)
2022-08-08 09:45:56 -04:00
call s:Map('c', '<C-R><C-F>', '<Plug><cfile>', '<unique>', 1)
2018-06-14 06:31:12 -04:00
endif
endfunction
2018-07-30 17:18:16 -04:00
function! s:ContainingCommit() abort
2018-08-25 12:13:42 -04:00
let commit = s:Owner(@%)
2021-05-05 04:25:00 -04:00
return empty(commit) ? '@' : commit
2018-06-14 06:31:12 -04:00
endfunction
2019-08-22 11:36:17 -04:00
function! s:SquashArgument(...) abort
2019-01-08 05:11:54 -05:00
if &filetype == 'fugitive'
2020-04-25 21:56:16 -04:00
let commit = matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze \|^' . s:ref_header . ': \zs\S\+')
2019-08-22 11:36:17 -04:00
elseif has_key(s:temp_files, s:cpath(expand('%:p')))
2021-05-31 16:30:26 -04:00
let commit = matchstr(getline('.'), '\S\@<!\x\{4,\}\>')
2019-01-08 05:11:54 -05:00
else
2019-08-22 11:36:17 -04:00
let commit = s:Owner(@%)
2019-01-08 05:11:54 -05:00
endif
2019-08-22 11:36:17 -04:00
return len(commit) && a:0 ? printf(a:1, commit) : commit
endfunction
function! s:RebaseArgument() abort
return s:SquashArgument(' %s^')
2019-01-08 05:11:54 -05:00
endfunction
2018-07-30 17:18:16 -04:00
function! s:NavigateUp(count) abort
2018-08-25 12:13:42 -04:00
let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
2018-07-30 17:18:16 -04:00
let c = a:count
while c
if rev =~# ':.*/.'
let rev = matchstr(rev, '.*\ze/.\+', '')
elseif rev =~# '.:.'
let rev = matchstr(rev, '^.[^:]*:')
elseif rev =~# '^:'
2021-05-05 04:25:00 -04:00
let rev = '@^{}'
2018-07-30 17:18:16 -04:00
elseif rev =~# ':$'
let rev = rev[0:-2]
2018-06-14 06:31:12 -04:00
else
2018-07-30 17:18:16 -04:00
return rev.'~'.c
2018-06-14 06:31:12 -04:00
endif
2018-07-30 17:18:16 -04:00
let c -= 1
endwhile
return rev
2018-06-14 06:31:12 -04:00
endfunction
2022-08-08 09:45:56 -04:00
function! s:ParseDiffHeader(str) abort
let list = matchlist(a:str, '\Cdiff --git \("\=[^/].*\|/dev/null\) \("\=[^/].*\|/dev/null\)$')
return [fugitive#Unquote(get(list, 1, '')), fugitive#Unquote(get(list, 2, ''))]
endfunction
function! s:HunkPosition(lnum) abort
let lnum = a:lnum + get({'@': 1, '\': -1}, getline(a:lnum)[0], 0)
let offsets = {' ': -1, '+': 0, '-': 0}
let sigil = getline(lnum)[0]
let line_char = sigil
while has_key(offsets, line_char)
let offsets[line_char] += 1
let lnum -= 1
let line_char = getline(lnum)[0]
endwhile
let starts = matchlist(getline(lnum), '^@@\+[ 0-9,-]* -\(\d\+\),\d\+ +\(\d\+\),')
if empty(starts)
return [0, 0, 0]
endif
return [lnum,
\ sigil ==# '+' ? 0 : starts[1] + offsets[' '] + offsets['-'],
\ sigil ==# '-' ? 0 : starts[2] + offsets[' '] + offsets['+']]
endfunction
2019-08-22 11:36:17 -04:00
function! s:MapMotion(lhs, rhs) abort
2021-10-11 05:30:43 -04:00
let maps = [
\ s:Map('n', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>"),
\ s:Map('o', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>"),
\ s:Map('x', a:lhs, ":<C-U>exe 'normal! gv'<Bar>" . a:rhs . "<CR>", "<silent>")]
call filter(maps, '!empty(v:val)')
return join(maps, '|')
2019-08-22 11:36:17 -04:00
endfunction
2018-07-30 17:18:16 -04:00
function! fugitive#MapJumps(...) abort
if !&modifiable
if get(b:, 'fugitive_type', '') ==# 'blob'
2021-10-11 05:30:43 -04:00
let blame_tail = '<C-R>=v:count ? " --reverse" : ""<CR><CR>'
exe s:Map('n', '<2-LeftMouse>', ':<C-U>0,1Git ++curwin blame' . blame_tail, '<silent>')
exe s:Map('n', '<CR>', ':<C-U>0,1Git ++curwin blame' . blame_tail, '<silent>')
exe s:Map('n', 'o', ':<C-U>0,1Git blame' . blame_tail, '<silent>')
exe s:Map('n', 'p', ':<C-U>0,1Git blame!' . blame_tail, '<silent>')
if has('patch-7.4.1898')
exe s:Map('n', 'gO', ':<C-U>vertical 0,1Git blame' . blame_tail, '<silent>')
exe s:Map('n', 'O', ':<C-U>tab 0,1Git blame' . blame_tail, '<silent>')
else
exe s:Map('n', 'gO', ':<C-U>0,4Git blame' . blame_tail, '<silent>')
exe s:Map('n', 'O', ':<C-U>0,5Git blame' . blame_tail, '<silent>')
endif
call s:Map('n', 'D', ":echoerr 'fugitive: D has been removed in favor of dd'<CR>", '<silent><unique>')
2020-12-04 16:15:32 -05:00
call s:Map('n', 'dd', ":<C-U>call fugitive#DiffClose()<Bar>Gdiffsplit!<CR>", '<silent>')
call s:Map('n', 'dh', ":<C-U>call fugitive#DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
call s:Map('n', 'ds', ":<C-U>call fugitive#DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
call s:Map('n', 'dv', ":<C-U>call fugitive#DiffClose()<Bar>Gvdiffsplit!<CR>", '<silent>')
2019-08-22 11:36:17 -04:00
call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
2018-06-14 06:31:12 -04:00
else
2019-08-22 11:36:17 -04:00
call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
call s:Map('n', '<CR>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
call s:Map('n', 'o', ':<C-U>exe <SID>GF("split")<CR>', '<silent>')
call s:Map('n', 'gO', ':<C-U>exe <SID>GF("vsplit")<CR>', '<silent>')
call s:Map('n', 'O', ':<C-U>exe <SID>GF("tabedit")<CR>', '<silent>')
call s:Map('n', 'p', ':<C-U>exe <SID>GF("pedit")<CR>', '<silent>')
if !exists('g:fugitive_no_maps')
2021-10-11 05:30:43 -04:00
call s:Map('n', '<C-P>', ':exe <SID>PreviousItem(v:count1)<Bar>echohl WarningMsg<Bar>echo "CTRL-P is deprecated in favor of ("<Bar>echohl NONE<CR>', '<unique>')
call s:Map('n', '<C-N>', ':exe <SID>NextItem(v:count1)<Bar>echohl WarningMsg<Bar>echo "CTRL-N is deprecated in favor of )"<Bar>echohl NONE<CR>', '<unique>')
2019-08-22 11:36:17 -04:00
endif
call s:MapMotion('(', 'exe <SID>PreviousItem(v:count1)')
call s:MapMotion(')', 'exe <SID>NextItem(v:count1)')
call s:MapMotion('K', 'exe <SID>PreviousHunk(v:count1)')
call s:MapMotion('J', 'exe <SID>NextHunk(v:count1)')
call s:MapMotion('[c', 'exe <SID>PreviousHunk(v:count1)')
call s:MapMotion(']c', 'exe <SID>NextHunk(v:count1)')
call s:MapMotion('[/', 'exe <SID>PreviousFile(v:count1)')
call s:MapMotion(']/', 'exe <SID>NextFile(v:count1)')
call s:MapMotion('[m', 'exe <SID>PreviousFile(v:count1)')
call s:MapMotion(']m', 'exe <SID>NextFile(v:count1)')
call s:MapMotion('[[', 'exe <SID>PreviousSection(v:count1)')
call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
endif
2021-10-11 05:30:43 -04:00
call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent><unique>')
2020-12-04 16:15:32 -05:00
call s:Map('n', 'dq', ":<C-U>call fugitive#DiffClose()<CR>", '<silent>')
2019-08-22 11:36:17 -04:00
call s:Map('n', '-', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>NavigateUp(v:count1))<Bar> if getline(1) =~# '^tree \x\{40,\}$' && empty(getline(2))<Bar>call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')<Bar>endif<CR>", '<silent>')
call s:Map('n', 'P', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
call s:Map('n', '~', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
call s:Map('n', 'C', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
2021-10-11 05:30:43 -04:00
call s:Map('n', 'cp', ":<C-U>echoerr 'Use gC'<CR>", '<silent><unique>')
2019-08-22 11:36:17 -04:00
call s:Map('n', 'gC', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
call s:Map('n', 'gc', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
call s:Map('n', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
call s:Map('x', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
2020-04-25 21:56:16 -04:00
call s:Map('n', 'c<Space>', ':Git commit<Space>')
call s:Map('n', 'c<CR>', ':Git commit<CR>')
call s:Map('n', 'cv<Space>', ':tab Git commit -v<Space>')
call s:Map('n', 'cv<CR>', ':tab Git commit -v<CR>')
call s:Map('n', 'ca', ':<C-U>Git commit --amend<CR>', '<silent>')
call s:Map('n', 'cc', ':<C-U>Git commit<CR>', '<silent>')
call s:Map('n', 'ce', ':<C-U>Git commit --amend --no-edit<CR>', '<silent>')
call s:Map('n', 'cw', ':<C-U>Git commit --amend --only<CR>', '<silent>')
call s:Map('n', 'cva', ':<C-U>tab Git commit -v --amend<CR>', '<silent>')
call s:Map('n', 'cvc', ':<C-U>tab Git commit -v<CR>', '<silent>')
call s:Map('n', 'cRa', ':<C-U>Git commit --reset-author --amend<CR>', '<silent>')
call s:Map('n', 'cRe', ':<C-U>Git commit --reset-author --amend --no-edit<CR>', '<silent>')
call s:Map('n', 'cRw', ':<C-U>Git commit --reset-author --amend --only<CR>', '<silent>')
call s:Map('n', 'cf', ':<C-U>Git commit --fixup=<C-R>=<SID>SquashArgument()<CR>')
call s:Map('n', 'cF', ':<C-U><Bar>Git -c sequence.editor=true rebase --interactive --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Git commit --fixup=<C-R>=<SID>SquashArgument()<CR>')
call s:Map('n', 'cs', ':<C-U>Git commit --no-edit --squash=<C-R>=<SID>SquashArgument()<CR>')
call s:Map('n', 'cS', ':<C-U><Bar>Git -c sequence.editor=true rebase --interactive --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Git commit --no-edit --squash=<C-R>=<SID>SquashArgument()<CR>')
call s:Map('n', 'cA', ':<C-U>Git commit --edit --squash=<C-R>=<SID>SquashArgument()<CR>')
call s:Map('n', 'c?', ':<C-U>help fugitive_c<CR>', '<silent>')
call s:Map('n', 'cr<Space>', ':Git revert<Space>')
call s:Map('n', 'cr<CR>', ':Git revert<CR>')
call s:Map('n', 'crc', ':<C-U>Git revert <C-R>=<SID>SquashArgument()<CR><CR>', '<silent>')
call s:Map('n', 'crn', ':<C-U>Git revert --no-commit <C-R>=<SID>SquashArgument()<CR><CR>', '<silent>')
call s:Map('n', 'cr?', ':<C-U>help fugitive_cr<CR>', '<silent>')
call s:Map('n', 'cm<Space>', ':Git merge<Space>')
call s:Map('n', 'cm<CR>', ':Git merge<CR>')
call s:Map('n', 'cmt', ':Git mergetool')
call s:Map('n', 'cm?', ':<C-U>help fugitive_cm<CR>', '<silent>')
call s:Map('n', 'cz<Space>', ':Git stash<Space>')
call s:Map('n', 'cz<CR>', ':Git stash<CR>')
call s:Map('n', 'cza', ':<C-U>Git stash apply --quiet --index stash@{<C-R>=v:count<CR>}<CR>')
call s:Map('n', 'czA', ':<C-U>Git stash apply --quiet stash@{<C-R>=v:count<CR>}<CR>')
call s:Map('n', 'czp', ':<C-U>Git stash pop --quiet --index stash@{<C-R>=v:count<CR>}<CR>')
call s:Map('n', 'czP', ':<C-U>Git stash pop --quiet stash@{<C-R>=v:count<CR>}<CR>')
2022-08-08 09:45:56 -04:00
call s:Map('n', 'czs', ':<C-U>Git stash push --staged<CR>')
2020-04-25 21:56:16 -04:00
call s:Map('n', 'czv', ':<C-U>exe "Gedit" fugitive#RevParse("stash@{" . v:count . "}")<CR>', '<silent>')
2022-08-08 09:45:56 -04:00
call s:Map('n', 'czw', ':<C-U>Git stash push --keep-index<C-R>=v:count > 1 ? " --all" : v:count ? " --include-untracked" : ""<CR><CR>')
call s:Map('n', 'czz', ':<C-U>Git stash push <C-R>=v:count > 1 ? " --all" : v:count ? " --include-untracked" : ""<CR><CR>')
2020-04-25 21:56:16 -04:00
call s:Map('n', 'cz?', ':<C-U>help fugitive_cz<CR>', '<silent>')
call s:Map('n', 'co<Space>', ':Git checkout<Space>')
call s:Map('n', 'co<CR>', ':Git checkout<CR>')
2021-05-05 04:25:00 -04:00
call s:Map('n', 'coo', ':<C-U>Git checkout <C-R>=substitute(<SID>SquashArgument(),"^$",get(<SID>TempState(),"filetype","") ==# "git" ? expand("<cfile>") : "","")<CR> --<CR>')
2020-04-25 21:56:16 -04:00
call s:Map('n', 'co?', ':<C-U>help fugitive_co<CR>', '<silent>')
call s:Map('n', 'cb<Space>', ':Git branch<Space>')
call s:Map('n', 'cb<CR>', ':Git branch<CR>')
call s:Map('n', 'cb?', ':<C-U>help fugitive_cb<CR>', '<silent>')
call s:Map('n', 'r<Space>', ':Git rebase<Space>')
call s:Map('n', 'r<CR>', ':Git rebase<CR>')
call s:Map('n', 'ri', ':<C-U>Git rebase --interactive<C-R>=<SID>RebaseArgument()<CR><CR>', '<silent>')
call s:Map('n', 'rf', ':<C-U>Git -c sequence.editor=true rebase --interactive --autosquash<C-R>=<SID>RebaseArgument()<CR><CR>', '<silent>')
call s:Map('n', 'ru', ':<C-U>Git rebase --interactive @{upstream}<CR>', '<silent>')
call s:Map('n', 'rp', ':<C-U>Git rebase --interactive @{push}<CR>', '<silent>')
call s:Map('n', 'rw', ':<C-U>Git rebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/reword/e<CR>', '<silent>')
call s:Map('n', 'rm', ':<C-U>Git rebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/edit/e<CR>', '<silent>')
call s:Map('n', 'rd', ':<C-U>Git rebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>', '<silent>')
call s:Map('n', 'rk', ':<C-U>Git rebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>', '<silent>')
call s:Map('n', 'rx', ':<C-U>Git rebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>', '<silent>')
call s:Map('n', 'rr', ':<C-U>Git rebase --continue<CR>', '<silent>')
call s:Map('n', 'rs', ':<C-U>Git rebase --skip<CR>', '<silent>')
call s:Map('n', 're', ':<C-U>Git rebase --edit-todo<CR>', '<silent>')
call s:Map('n', 'ra', ':<C-U>Git rebase --abort<CR>', '<silent>')
call s:Map('n', 'r?', ':<C-U>help fugitive_r<CR>', '<silent>')
2019-08-22 11:36:17 -04:00
call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
2019-11-16 10:28:42 -05:00
call s:Map('n', 'g?', ":<C-U>help fugitive-map<CR>", '<silent>')
call s:Map('n', '<F1>', ":<C-U>help fugitive-map<CR>", '<silent>')
2018-07-30 17:18:16 -04:00
endif
2021-05-05 04:25:00 -04:00
let old_browsex = maparg('<Plug>NetrwBrowseX', 'n')
let new_browsex = substitute(old_browsex, '\Cnetrw#CheckIfRemote(\%(netrw#GX()\)\=)', '0', 'g')
let new_browsex = substitute(new_browsex, 'netrw#GX()\|expand((exists("g:netrw_gx")? g:netrw_gx : ''<cfile>''))', 'fugitive#GX()', 'g')
if new_browsex !=# old_browsex
exe 'nnoremap <silent> <buffer> <Plug>NetrwBrowseX' new_browsex
endif
endfunction
function! fugitive#GX() abort
try
2021-10-11 05:30:43 -04:00
let results = &filetype ==# 'fugitive' ? s:CfilePorcelain() : &filetype ==# 'git' ? s:cfile() : []
2021-05-05 04:25:00 -04:00
if len(results) && len(results[0])
return FugitiveReal(s:Generate(results[0]))
endif
catch /^fugitive:/
endtry
return expand(get(g:, 'netrw_gx', expand('<cfile>')))
2018-06-14 06:31:12 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:CfilePorcelain(...) abort
2019-03-08 06:04:56 -05:00
let tree = s:Tree()
2021-05-05 04:25:00 -04:00
if empty(tree)
return ['']
endif
2019-01-08 05:11:54 -05:00
let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
let info = s:StageInfo()
let line = getline('.')
2019-08-22 11:36:17 -04:00
if len(info.sigil) && len(info.section) && len(info.paths)
2019-01-08 05:11:54 -05:00
if info.section ==# 'Unstaged' && info.sigil !=# '-'
2019-08-22 11:36:17 -04:00
return [lead . info.relative[0], info.offset, 'normal!zv']
2019-01-08 05:11:54 -05:00
elseif info.section ==# 'Staged' && info.sigil ==# '-'
2019-08-22 11:36:17 -04:00
return ['@:' . info.relative[0], info.offset, 'normal!zv']
2019-01-08 05:11:54 -05:00
else
2019-08-22 11:36:17 -04:00
return [':0:' . info.relative[0], info.offset, 'normal!zv']
2019-01-08 05:11:54 -05:00
endif
2019-08-22 11:36:17 -04:00
elseif len(info.paths)
return [lead . info.relative[0]]
2019-01-08 05:11:54 -05:00
elseif len(info.commit)
return [info.commit]
2020-04-25 21:56:16 -04:00
elseif line =~# '^' . s:ref_header . ': \|^Head: '
2019-01-08 05:11:54 -05:00
return [matchstr(line, ' \zs.*')]
else
return ['']
endif
endfunction
2021-10-11 05:30:43 -04:00
function! fugitive#PorcelainCfile() abort
let file = fugitive#Find(s:CfilePorcelain()[0])
2019-01-08 05:11:54 -05:00
return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
endfunction
2021-10-11 05:30:43 -04:00
function! s:StatusCfile(...) abort
2019-03-08 06:04:56 -05:00
let tree = s:Tree()
2021-05-05 04:25:00 -04:00
if empty(tree)
2022-08-08 09:45:56 -04:00
return []
2021-05-05 04:25:00 -04:00
endif
2018-08-25 12:13:42 -04:00
let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
2018-07-30 17:18:16 -04:00
if getline('.') =~# '^.\=\trenamed:.* -> '
2021-10-11 05:30:43 -04:00
return [lead . matchstr(getline('.'),' -> \zs.*')]
2018-07-30 17:18:16 -04:00
elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
2021-10-11 05:30:43 -04:00
return [lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')]
2018-07-30 17:18:16 -04:00
elseif getline('.') =~# '^.\=\t.'
2021-10-11 05:30:43 -04:00
return [lead . matchstr(getline('.'),'\t\zs.*')]
2018-07-30 17:18:16 -04:00
elseif getline('.') =~# ': needs merge$'
2021-10-11 05:30:43 -04:00
return [lead . matchstr(getline('.'),'.*\ze: needs merge$')]
2018-07-30 17:18:16 -04:00
elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
2021-10-11 05:30:43 -04:00
return ['HEAD']
2018-07-30 17:18:16 -04:00
elseif getline('.') =~# '^\%(. \)\=On branch '
2021-10-11 05:30:43 -04:00
return ['refs/heads/'.getline('.')[12:]]
2018-07-30 17:18:16 -04:00
elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
2021-10-11 05:30:43 -04:00
return [matchstr(getline('.'),"'\\zs\\S\\+\\ze'")]
2018-07-30 17:18:16 -04:00
else
2021-10-11 05:30:43 -04:00
return []
2018-06-14 06:31:12 -04:00
endif
endfunction
2019-01-08 05:11:54 -05:00
function! fugitive#MessageCfile() abort
2021-10-11 05:30:43 -04:00
let file = fugitive#Find(get(s:StatusCfile(), 0, ''))
2018-09-24 20:40:17 -04:00
return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
2018-06-14 06:31:12 -04:00
endfunction
2021-10-11 05:30:43 -04:00
function! s:BranchCfile(result) abort
return matchstr(getline('.'), '^. \zs\S\+')
endfunction
2022-08-08 09:45:56 -04:00
let s:diff_header_pattern = '^diff --git \%("\=[abciow12]/.*\|/dev/null\) \%("\=[abciow12]/.*\|/dev/null\)$'
2018-06-14 06:31:12 -04:00
function! s:cfile() abort
2021-10-11 05:30:43 -04:00
let temp_state = s:TempState()
let name = substitute(get(get(temp_state, 'args', []), 0, ''), '\%(^\|-\)\(\l\)', '\u\1', 'g')
if exists('*s:' . name . 'Cfile')
let cfile = s:{name}Cfile(temp_state)
if !empty(cfile)
return type(cfile) == type('') ? [cfile] : cfile
endif
endif
2021-05-05 04:25:00 -04:00
if empty(FugitiveGitDir())
return []
endif
2018-06-14 06:31:12 -04:00
try
2018-07-30 17:18:16 -04:00
let myhash = s:DirRev(@%)[1]
if len(myhash)
try
let myhash = fugitive#RevParse(myhash)
catch /^fugitive:/
let myhash = ''
endtry
endif
2021-10-11 05:30:43 -04:00
if empty(myhash) && get(temp_state, 'filetype', '') ==# 'git'
2021-05-05 04:25:00 -04:00
let lnum = line('.')
while lnum > 0
if getline(lnum) =~# '^\%(commit\|tag\) \w'
let myhash = matchstr(getline(lnum),'^\w\+ \zs\S\+')
break
endif
let lnum -= 1
endwhile
2018-06-14 06:31:12 -04:00
endif
2018-07-30 17:18:16 -04:00
let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
2018-06-14 06:31:12 -04:00
2018-07-30 17:18:16 -04:00
let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
\ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
2019-08-22 11:36:17 -04:00
if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40,\}\t'
2018-07-30 17:18:16 -04:00
return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
elseif showtree
return [treebase . s:sub(getline('.'),'/$','')]
2018-06-14 06:31:12 -04:00
else
let dcmds = []
" Index
2019-08-22 11:36:17 -04:00
if getline('.') =~# '^\d\{6\} \x\{40,\} \d\t'
let ref = matchstr(getline('.'),'\x\{40,\}')
2018-06-14 06:31:12 -04:00
let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
return [file]
endif
if getline('.') =~# '^ref: '
let ref = strpart(getline('.'),5)
2021-10-11 05:30:43 -04:00
elseif getline('.') =~# '^\%([|/\\_ ]*\*[|/\\_ ]*\)\=commit \x\{40,\}\>'
2019-08-22 11:36:17 -04:00
let ref = matchstr(getline('.'),'\x\{40,\}')
2018-06-14 06:31:12 -04:00
return [ref]
2019-08-22 11:36:17 -04:00
elseif getline('.') =~# '^parent \x\{40,\}\>'
let ref = matchstr(getline('.'),'\x\{40,\}')
2018-06-14 06:31:12 -04:00
let line = line('.')
let parent = 0
while getline(line) =~# '^parent '
let parent += 1
let line -= 1
endwhile
return [ref]
2019-08-22 11:36:17 -04:00
elseif getline('.') =~# '^tree \x\{40,\}$'
let ref = matchstr(getline('.'),'\x\{40,\}')
2018-07-30 17:18:16 -04:00
if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
2018-06-14 06:31:12 -04:00
let ref = myhash.':'
endif
return [ref]
2019-08-22 11:36:17 -04:00
elseif getline('.') =~# '^object \x\{40,\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
let ref = matchstr(getline('.'),'\x\{40,\}')
2018-06-14 06:31:12 -04:00
let type = matchstr(getline(line('.')+1),'type \zs.*')
elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
2018-08-25 12:13:42 -04:00
let ref = s:DirRev(@%)[1]
2018-06-14 06:31:12 -04:00
2019-08-22 11:36:17 -04:00
elseif getline('.') =~# '^\l\{3,8\} \x\{40,\}\>'
let ref = matchstr(getline('.'),'\x\{40,\}')
2018-06-14 06:31:12 -04:00
echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
2021-10-11 05:30:43 -04:00
elseif getline('.') =~# '^[A-Z]\d*\t\S' && len(myhash)
let files = split(getline('.'), "\t")[1:-1]
let ref = 'b/' . files[-1]
if getline('.') =~# '^D'
let ref = 'a/' . files[0]
elseif getline('.') !~# '^A'
let dcmds = ['', 'Gdiffsplit! >' . myhash . '^:' . fnameescape(files[0])]
endif
2022-08-08 09:45:56 -04:00
elseif getline('.') =~# '^[+-]'
let [header_lnum, old_lnum, new_lnum] = s:HunkPosition(line('.'))
if new_lnum > 0
let ref = s:ParseDiffHeader(getline(search(s:diff_header_pattern, 'bnW')))[1]
let dcmds = [new_lnum, 'normal!zv']
elseif old_lnum > 0
let ref = s:ParseDiffHeader(getline(search(s:diff_header_pattern, 'bnW')))[0]
let dcmds = [old_lnum, 'normal!zv']
else
let ref = fugitive#Unquote(matchstr(getline('.'), '\C[+-]\{3\} \zs"\=[abciow12]/.*'))
endif
2018-06-14 06:31:12 -04:00
elseif getline('.') =~# '^rename from '
let ref = 'a/'.getline('.')[12:]
elseif getline('.') =~# '^rename to '
let ref = 'b/'.getline('.')[10:]
2018-07-04 06:53:25 -04:00
elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
2022-08-08 09:45:56 -04:00
let diff = getline(search(s:diff_header_pattern, 'bcnW'))
2018-06-14 06:31:12 -04:00
let offset = matchstr(getline('.'), '+\zs\d\+')
2022-08-08 09:45:56 -04:00
let [dref, ref] = s:ParseDiffHeader(diff)
2019-08-22 11:36:17 -04:00
let dcmd = 'Gdiffsplit! +'.offset
2018-06-14 06:31:12 -04:00
2022-08-08 09:45:56 -04:00
elseif getline('.') =~# s:diff_header_pattern
let [dref, ref] = s:ParseDiffHeader(getline('.'))
2019-08-22 11:36:17 -04:00
let dcmd = 'Gdiffsplit!'
2018-06-14 06:31:12 -04:00
2022-08-08 09:45:56 -04:00
elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# s:diff_header_pattern
let [dref, ref] = s:ParseDiffHeader(getline(line('.') - '.'))
2019-08-22 11:36:17 -04:00
let dcmd = 'Gdiffsplit!'
2018-06-14 06:31:12 -04:00
2019-08-22 11:36:17 -04:00
elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
2018-06-14 06:31:12 -04:00
let ref = getline('.')
2019-08-22 11:36:17 -04:00
elseif expand('<cword>') =~# '^\x\{7,\}\>'
2018-06-14 06:31:12 -04:00
return [expand('<cword>')]
else
let ref = ''
endif
let prefixes = {
\ '1': '',
\ '2': '',
\ 'b': ':0:',
\ 'i': ':0:',
\ 'o': '',
\ 'w': ''}
if len(myhash)
let prefixes.a = myhash.'^:'
let prefixes.b = myhash.':'
endif
2021-05-05 04:25:00 -04:00
let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "@:")', '')
2018-06-14 06:31:12 -04:00
if exists('dref')
2021-05-05 04:25:00 -04:00
let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "@:")', '')
2018-06-14 06:31:12 -04:00
endif
if ref ==# '/dev/null'
" Empty blob
let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
endif
if exists('dref')
2021-10-11 05:30:43 -04:00
return [ref, dcmd . ' >' . s:fnameescape(dref)] + dcmds
2018-06-14 06:31:12 -04:00
elseif ref != ""
return [ref] + dcmds
endif
endif
return []
endtry
endfunction
function! s:GF(mode) abort
try
2021-10-11 05:30:43 -04:00
let results = &filetype ==# 'fugitive' ? s:CfilePorcelain() : &filetype ==# 'gitcommit' ? s:StatusCfile() : s:cfile()
2018-06-14 06:31:12 -04:00
catch /^fugitive:/
2019-08-22 11:36:17 -04:00
return 'echoerr ' . string(v:exception)
2018-06-14 06:31:12 -04:00
endtry
2018-07-30 17:18:16 -04:00
if len(results) > 1
2021-10-11 05:30:43 -04:00
let cmd = 'G' . a:mode .
2022-08-08 09:45:56 -04:00
\ (empty(results[1]) ? '' : ' +' . s:PlusEscape(results[1])) . ' ' .
2021-10-11 05:30:43 -04:00
\ fnameescape(results[0])
let tail = join(map(results[2:-1], '"|" . v:val'), '')
if a:mode ==# 'pedit' && len(tail)
return cmd . '|wincmd P|exe ' . string(tail[1:-1]) . '|wincmd p'
else
return cmd . tail
endif
2019-01-08 05:11:54 -05:00
elseif len(results) && len(results[0])
2018-07-30 17:18:16 -04:00
return 'G' . a:mode . ' ' . s:fnameescape(results[0])
2018-06-14 06:31:12 -04:00
else
return ''
endif
endfunction
function! fugitive#Cfile() abort
let pre = ''
let results = s:cfile()
if empty(results)
2021-10-11 05:30:43 -04:00
if !empty(s:TempState())
let cfile = s:TempDotMap()
if !empty(cfile)
return fnameescape(s:Generate(cfile))
endif
endif
2018-06-14 06:31:12 -04:00
let cfile = expand('<cfile>')
if &includeexpr =~# '\<v:fname\>'
sandbox let cfile = eval(substitute(&includeexpr, '\C\<v:fname\>', '\=string(cfile)', 'g'))
endif
return cfile
elseif len(results) > 1
let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' '
endif
2021-10-11 05:30:43 -04:00
return pre . fnameescape(s:Generate(results[0]))
2018-06-14 06:31:12 -04:00
endfunction
" Section: Statusline
function! fugitive#Statusline(...) abort
2019-11-16 10:28:42 -05:00
let dir = s:Dir(bufnr(''))
if empty(dir)
2018-06-14 06:31:12 -04:00
return ''
endif
let status = ''
2018-07-30 17:18:16 -04:00
let commit = s:DirCommitFile(@%)[1]
if len(commit)
2018-09-24 20:40:17 -04:00
let status .= ':' . commit[0:6]
2018-06-14 06:31:12 -04:00
endif
2022-08-08 09:45:56 -04:00
let status .= '('.fugitive#Head(7, dir).')'
2018-07-30 17:18:16 -04:00
return '[Git'.status.']'
2018-06-14 06:31:12 -04:00
endfunction
function! fugitive#statusline(...) abort
return fugitive#Statusline()
endfunction
" Section: Folding
function! fugitive#Foldtext() abort
if &foldmethod !=# 'syntax'
return foldtext()
endif
let line_foldstart = getline(v:foldstart)
if line_foldstart =~# '^diff '
let [add, remove] = [-1, -1]
let filename = ''
for lnum in range(v:foldstart, v:foldend)
let line = getline(lnum)
2022-08-08 09:45:56 -04:00
if filename ==# '' && line =~# '^[+-]\{3\} "\=[abciow12]/'
let filename = fugitive#Unquote(line[4:-1])[2:-1]
2018-06-14 06:31:12 -04:00
endif
if line =~# '^+'
let add += 1
elseif line =~# '^-'
let remove += 1
elseif line =~# '^Binary '
let binary = 1
endif
endfor
if filename ==# ''
2022-08-08 09:45:56 -04:00
let filename = fugitive#Unquote(matchstr(line_foldstart, '^diff .\{-\} \zs"\=[abciow12]/\zs.*\ze "\=[abciow12]/'))[2:-1]
2018-06-14 06:31:12 -04:00
endif
if filename ==# ''
let filename = line_foldstart[5:-1]
endif
if exists('binary')
return 'Binary: '.filename
else
2021-05-05 04:25:00 -04:00
return '+-' . v:folddashes . ' ' . (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
2018-06-14 06:31:12 -04:00
endif
2021-05-05 04:25:00 -04:00
elseif line_foldstart =~# '^@@\+ .* @@'
return '+-' . v:folddashes . ' ' . line_foldstart
elseif &filetype ==# 'gitcommit' && line_foldstart =~# '^# .*:$'
2018-06-14 06:31:12 -04:00
let lines = getline(v:foldstart, v:foldend)
call filter(lines, 'v:val =~# "^#\t"')
cal map(lines, "s:sub(v:val, '^#\t%(modified: +|renamed: +)=', '')")
cal map(lines, "s:sub(v:val, '^([[:alpha:] ]+): +(.*)', '\\2 (\\1)')")
return line_foldstart.' '.join(lines, ', ')
endif
return foldtext()
endfunction
function! fugitive#foldtext() abort
return fugitive#Foldtext()
endfunction
2018-08-25 12:13:42 -04:00
" Section: End