2bde1d9de6
This was done in d4fb2a2f5c
to fix
filetype modelines, but it appears to no longer be necessary and is
confusing other plugins.
4709 lines
157 KiB
VimL
4709 lines
157 KiB
VimL
" Location: autoload/fugitive.vim
|
|
" Maintainer: Tim Pope <http://tpo.pe/>
|
|
|
|
if exists('g:autoloaded_fugitive')
|
|
finish
|
|
endif
|
|
let g:autoloaded_fugitive = 1
|
|
|
|
if !exists('g:fugitive_git_executable')
|
|
let g:fugitive_git_executable = 'git'
|
|
endif
|
|
|
|
" Section: Utility
|
|
|
|
function! s:function(name) abort
|
|
return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
|
|
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
|
|
|
|
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
|
|
|
|
function! s:winshell() abort
|
|
return has('win32') && &shellcmdflag !~# '^-'
|
|
endfunction
|
|
|
|
function! s:shellesc(arg) abort
|
|
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
|
|
return a:arg
|
|
elseif s:winshell()
|
|
return '"'.s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"').'"'
|
|
else
|
|
return shellescape(a:arg)
|
|
endif
|
|
endfunction
|
|
|
|
let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
|
|
function! s:fnameescape(file) abort
|
|
if exists('*fnameescape')
|
|
return fnameescape(a:file)
|
|
else
|
|
return escape(a:file, s:fnameescape)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:throw(string) abort
|
|
let v:errmsg = 'fugitive: '.a:string
|
|
throw v:errmsg
|
|
endfunction
|
|
|
|
function! s:warn(str) abort
|
|
echohl WarningMsg
|
|
echomsg a:str
|
|
echohl None
|
|
let v:warningmsg = a:str
|
|
endfunction
|
|
|
|
function! s:Slash(path) abort
|
|
if exists('+shellslash')
|
|
return tr(a:path, '\', '/')
|
|
else
|
|
return a:path
|
|
endif
|
|
endfunction
|
|
|
|
function! s:PlatformSlash(path) abort
|
|
if exists('+shellslash') && !&shellslash
|
|
return tr(a:path, '/', '\')
|
|
else
|
|
return a:path
|
|
endif
|
|
endfunction
|
|
|
|
function! s:Resolve(path) abort
|
|
let path = resolve(a:path)
|
|
if has('win32')
|
|
let path = s:PlatformSlash(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t'))
|
|
endif
|
|
return path
|
|
endfunction
|
|
|
|
function! s:cpath(path, ...) abort
|
|
if exists('+fileignorecase') && &fileignorecase
|
|
let path = s:PlatformSlash(tolower(a:path))
|
|
else
|
|
let path = s:PlatformSlash(a:path)
|
|
endif
|
|
return a:0 ? path ==# s:cpath(a:1) : path
|
|
endfunction
|
|
|
|
function! s:Cd(...) abort
|
|
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'
|
|
if !a:0
|
|
return cd
|
|
endif
|
|
let cwd = getcwd()
|
|
if s:cpath(cwd, a:1)
|
|
return ''
|
|
endif
|
|
exe cd s:fnameescape(a:1)
|
|
return cd . ' ' . s:fnameescape(cwd)
|
|
endfunction
|
|
|
|
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
|
|
|
|
function! s:map(mode, lhs, rhs, ...) abort
|
|
let flags = (a:0 ? a:1 : '') . (a:rhs =~# '<Plug>' ? '' : '<script>')
|
|
let head = a:lhs
|
|
let tail = ''
|
|
let keys = get(g:, a:mode.'remap', {})
|
|
if type(keys) == type([])
|
|
return
|
|
endif
|
|
while !empty(head)
|
|
if has_key(keys, head)
|
|
let head = keys[head]
|
|
if empty(head)
|
|
return
|
|
endif
|
|
break
|
|
endif
|
|
let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
|
|
let head = substitute(head, '<[^<>]*>$\|.$', '', '')
|
|
endwhile
|
|
if flags !~# '<unique>' || empty(mapcheck(head.tail, a:mode))
|
|
exe a:mode.'map <buffer>' flags head.tail a:rhs
|
|
if a:0 > 1
|
|
let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') .
|
|
\ '|sil! exe "' . a:mode . 'unmap <buffer> ' . head.tail . '"'
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
function! s:System(cmd) abort
|
|
try
|
|
return system(a:cmd)
|
|
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)')
|
|
call s:throw('failed to run `' . a:cmd . '` with ' . join(opts, ' '))
|
|
endtry
|
|
endfunction
|
|
|
|
" Section: Git
|
|
|
|
function! s:UserCommand() abort
|
|
return get(g:, 'fugitive_git_command', g:fugitive_git_executable)
|
|
endfunction
|
|
|
|
function! s:Prepare(...) abort
|
|
return call('fugitive#Prepare', a:000)
|
|
endfunction
|
|
|
|
let s:git_versions = {}
|
|
function! fugitive#GitVersion(...) abort
|
|
if !has_key(s:git_versions, g:fugitive_git_executable)
|
|
let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), '\d\S\+')
|
|
endif
|
|
if !a:0
|
|
return s:git_versions[g:fugitive_git_executable]
|
|
endif
|
|
let components = split(s:git_versions[g:fugitive_git_executable], '\D\+')
|
|
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)
|
|
endfunction
|
|
|
|
let s:commondirs = {}
|
|
function! fugitive#CommonDir(dir) abort
|
|
if empty(a:dir)
|
|
return ''
|
|
endif
|
|
if !has_key(s:commondirs, a:dir)
|
|
if getfsize(a:dir . '/HEAD') < 10
|
|
let s:commondirs[a:dir] = ''
|
|
elseif filereadable(a:dir . '/commondir')
|
|
let dir = get(readfile(a:dir . '/commondir', 1), 0, '')
|
|
if dir =~# '^/\|^\a:/'
|
|
let s:commondirs[a:dir] = dir
|
|
else
|
|
let s:commondirs[a:dir] = simplify(a:dir . '/' . dir)
|
|
endif
|
|
else
|
|
let s:commondirs[a:dir] = a:dir
|
|
endif
|
|
endif
|
|
return s:commondirs[a:dir]
|
|
endfunction
|
|
|
|
function! s:Dir(...) abort
|
|
return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
|
|
endfunction
|
|
|
|
function! s:Tree(...) abort
|
|
return a:0 ? FugitiveWorkTree(a:1) : FugitiveWorkTree()
|
|
endfunction
|
|
|
|
function! s:PreparePathArgs(cmd, dir, literal) abort
|
|
let literal_supported = fugitive#GitVersion(1, 9)
|
|
if a:literal && literal_supported
|
|
call insert(a:cmd, '--literal-pathspecs')
|
|
endif
|
|
let split = index(a:cmd, '--')
|
|
if split < 0
|
|
return a:cmd
|
|
endif
|
|
for i in range(split + 1, len(a:cmd) - 1)
|
|
if type(a:cmd[i]) == type(0)
|
|
let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
|
|
elseif a:literal
|
|
let a:cmd[i] = fugitive#Path(a:cmd[i], './', a:dir)
|
|
elseif !literal_supported
|
|
let a:cmd[i] = substitute(a:cmd[i], '^:\%(/\|([^)]*)\)\=:\=', './', '')
|
|
endif
|
|
endfor
|
|
return a:cmd
|
|
endfunction
|
|
|
|
let s:prepare_env = {
|
|
\ 'sequence.editor': 'GIT_SEQUENCE_EDITOR',
|
|
\ 'core.editor': 'GIT_EDITOR',
|
|
\ 'core.askpass': 'GIT_ASKPASS',
|
|
\ }
|
|
function! fugitive#Prepare(...) abort
|
|
if !a:0
|
|
return g:fugitive_git_executable
|
|
endif
|
|
if type(a:1) ==# type([])
|
|
let cmd = a:000[1:-1] + a:1
|
|
else
|
|
let cmd = copy(a:000)
|
|
endif
|
|
let pre = ''
|
|
let i = 0
|
|
while i < len(cmd)
|
|
if cmd[i] =~# '^$\|[\/.]' && cmd[i] !~# '^-'
|
|
let dir = remove(cmd, 0)
|
|
elseif type(cmd[i]) ==# type(0)
|
|
let dir = s:Dir(remove(cmd, i))
|
|
elseif cmd[i] ==# '-c' && len(cmd) > i + 1
|
|
let key = matchstr(cmd[i+1], '^[^=]*')
|
|
if has_key(s:prepare_env, tolower(key)) || key !~# '\.'
|
|
let var = get(s:prepare_env, tolower(key), key)
|
|
let val = matchstr(cmd[i+1], '=\zs.*')
|
|
if s:winshell()
|
|
let pre .= 'set ' . var . '=' . s:shellesc(val) . ' & '
|
|
else
|
|
let pre = (len(pre) ? pre : 'env ') . var . '=' . s:shellesc(val) . ' '
|
|
endif
|
|
endif
|
|
if fugitive#GitVersion(1, 8) && cmd[i+1] =~# '\.'
|
|
let i += 2
|
|
else
|
|
call remove(cmd, i, i + 1)
|
|
endif
|
|
elseif cmd[i] =~# '^--.*pathspecs$'
|
|
let explicit_pathspec_option = 1
|
|
if fugitive#GitVersion(1, 9)
|
|
let i += 1
|
|
else
|
|
call remove(cmd, i)
|
|
endif
|
|
elseif cmd[i] !~# '^-'
|
|
break
|
|
else
|
|
let i += 1
|
|
endif
|
|
endwhile
|
|
if !exists('dir')
|
|
let dir = s:Dir()
|
|
endif
|
|
let tree = s:Tree(dir)
|
|
call s:PreparePathArgs(cmd, dir, !exists('explicit_pathspec_option'))
|
|
let args = join(map(copy(cmd), 's:shellesc(v:val)'))
|
|
if empty(tree) || index(cmd, '--') == len(cmd) - 1
|
|
let args = s:shellesc('--git-dir=' . dir) . ' ' . args
|
|
elseif fugitive#GitVersion(1, 9)
|
|
let args = '-C ' . s:shellesc(tree) . ' ' . args
|
|
else
|
|
let pre = 'cd ' . s:shellesc(tree) . (s:winshell() ? ' & ' : '; ') . pre
|
|
endif
|
|
return pre . g:fugitive_git_executable . ' ' . args
|
|
endfunction
|
|
|
|
function! s:TreeChomp(...) abort
|
|
return s:sub(s:System(call('fugitive#Prepare', a:000)), '\n$', '')
|
|
endfunction
|
|
|
|
function! fugitive#Head(...) abort
|
|
let dir = a:0 > 1 ? a:2 : s:Dir()
|
|
if empty(dir) || !filereadable(dir . '/HEAD')
|
|
return ''
|
|
endif
|
|
let head = readfile(dir . '/HEAD')[0]
|
|
if head =~# '^ref: '
|
|
return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
|
|
elseif head =~# '^\x\{40\}$'
|
|
let len = a:0 ? a:1 : 0
|
|
return len < 0 ? head : len ? head[0:len-1] : ''
|
|
else
|
|
return ''
|
|
endif
|
|
endfunction
|
|
|
|
function! fugitive#RevParse(rev, ...) abort
|
|
let hash = system(s:Prepare(a:0 ? a:1 : s:Dir(), 'rev-parse', '--verify', a:rev, '--'))[0:-2]
|
|
if !v:shell_error && hash =~# '^\x\{40\}$'
|
|
return hash
|
|
endif
|
|
call s:throw('rev-parse '.a:rev.': '.hash)
|
|
endfunction
|
|
|
|
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
|
|
|
|
let s:config = {}
|
|
function! fugitive#Config(...) abort
|
|
let dir = s:Dir()
|
|
let name = ''
|
|
if a:0 >= 2 && type(a:2) == type({})
|
|
let name = substitute(a:1, '^[^.]\+\|[^.]\+$', '\L&', 'g')
|
|
return len(a:1) ? get(get(a:2, name, []), 0, '') : a:2
|
|
elseif a:0 >= 2
|
|
let dir = a:2
|
|
let name = a:1
|
|
elseif a:0 == 1 && type(a:1) == type({})
|
|
return a:1
|
|
elseif a:0 == 1 && a:1 =~# '^[[:alnum:]-]\+\.'
|
|
let name = a:1
|
|
elseif a:0 == 1
|
|
let dir = a:1
|
|
endif
|
|
let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
|
|
let key = len(dir) ? dir : '_'
|
|
if has_key(s:config, key) && s:config[key][0] ==# s:ConfigTimestamps(dir, s:config[key][1])
|
|
let dict = s:config[key][1]
|
|
else
|
|
let dict = {}
|
|
let lines = split(system(FugitivePrepare(['config', '--list', '-z'], dir)), "\1")
|
|
if v:shell_error
|
|
return {}
|
|
endif
|
|
for line in lines
|
|
let key = matchstr(line, "^[^\n]*")
|
|
if !has_key(dict, key)
|
|
let dict[key] = []
|
|
endif
|
|
call add(dict[key], strpart(line, len(key) + 1))
|
|
endfor
|
|
let s:config[dir] = [s:ConfigTimestamps(dir, dict), dict]
|
|
lockvar! dict
|
|
endif
|
|
return len(name) ? get(get(dict, name, []), 0, '') : dict
|
|
endfunction
|
|
|
|
function! s:Remote(dir) abort
|
|
let head = FugitiveHead(0, a:dir)
|
|
let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
|
|
let i = 10
|
|
while remote ==# '.' && i > 0
|
|
let head = matchstr(fugitive#Config('branch.' . head . '.merge'), 'refs/heads/\zs.*')
|
|
let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
|
|
let i -= 1
|
|
endwhile
|
|
return remote =~# '^\.\=$' ? 'origin' : remote
|
|
endfunction
|
|
|
|
function! fugitive#RemoteUrl(...) abort
|
|
let dir = a:0 > 1 ? a:2 : s:Dir()
|
|
let remote = !a:0 || a:1 =~# '^\.\=$' ? s:Remote(dir) : a:1
|
|
if !fugitive#GitVersion(2, 7)
|
|
return fugitive#Config('remote.' . remote . '.url')
|
|
endif
|
|
let cmd = s:Prepare(dir, 'remote', 'get-url', remote, '--')
|
|
let out = substitute(system(cmd), "\n$", '', '')
|
|
return v:shell_error ? '' : out
|
|
endfunction
|
|
|
|
" Section: Repository Object
|
|
|
|
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
|
|
|
|
let s:commands = []
|
|
function! s:command(definition, ...) abort
|
|
if a:0
|
|
call add(s:commands, a:definition . ' execute s:' . a:1 . "Command(<line1>, <line2>, +'<range>', <count>, <bang>0, '<mods>', <q-reg>, <q-args>, [<f-args>])")
|
|
else
|
|
call add(s:commands, a:definition)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:define_commands() abort
|
|
for command in s:commands
|
|
exe 'command! -buffer '.command
|
|
endfor
|
|
endfunction
|
|
|
|
let s:repo_prototype = {}
|
|
let s:repos = {}
|
|
|
|
function! fugitive#repo(...) abort
|
|
let dir = a:0 ? a:1 : (len(s:Dir()) ? s:Dir() : FugitiveExtractGitDir(expand('%:p')))
|
|
if dir !=# ''
|
|
if has_key(s:repos, dir)
|
|
let repo = get(s:repos, dir)
|
|
else
|
|
let repo = {'git_dir': dir}
|
|
let s:repos[dir] = repo
|
|
endif
|
|
return extend(repo, s:repo_prototype, 'keep')
|
|
endif
|
|
call s:throw('not a Git repository: ' . string(dir))
|
|
endfunction
|
|
|
|
function! s:repo_dir(...) dict abort
|
|
return join([self.git_dir]+a:000,'/')
|
|
endfunction
|
|
|
|
function! s:repo_tree(...) dict abort
|
|
let dir = s:Tree(self.git_dir)
|
|
if dir ==# ''
|
|
call s:throw('no work tree')
|
|
else
|
|
return join([dir]+a:000,'/')
|
|
endif
|
|
endfunction
|
|
|
|
function! s:repo_bare() dict abort
|
|
if self.dir() =~# '/\.git$'
|
|
return 0
|
|
else
|
|
return s:Tree(self.git_dir) ==# ''
|
|
endif
|
|
endfunction
|
|
|
|
function! s:repo_find(object) dict abort
|
|
return fugitive#Find(a:object, self.git_dir)
|
|
endfunction
|
|
|
|
function! s:repo_translate(rev) dict abort
|
|
return s:Slash(fugitive#Find(substitute(a:rev, '^/', ':(top)', ''), self.git_dir))
|
|
endfunction
|
|
|
|
function! s:repo_head(...) dict abort
|
|
return fugitive#Head(a:0 ? a:1 : 0, self.git_dir)
|
|
endfunction
|
|
|
|
call s:add_methods('repo',['dir','tree','bare','find','translate','head'])
|
|
|
|
function! s:repo_prepare(...) dict abort
|
|
return call('fugitive#Prepare', [self.git_dir] + a:000)
|
|
endfunction
|
|
|
|
function! s:repo_git_command(...) dict abort
|
|
let git = s:UserCommand() . ' --git-dir='.s:shellesc(self.git_dir)
|
|
return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
|
|
endfunction
|
|
|
|
function! s:repo_git_chomp(...) dict abort
|
|
let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
|
|
let output = git . join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
|
|
return s:sub(s:System(output),'\n$','')
|
|
endfunction
|
|
|
|
function! s:repo_git_chomp_in_tree(...) dict abort
|
|
let cdback = s:Cd(self.tree())
|
|
try
|
|
return call(self.git_chomp, a:000, self)
|
|
finally
|
|
execute cdback
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:repo_rev_parse(rev) dict abort
|
|
return fugitive#RevParse(a:rev, self.git_dir)
|
|
endfunction
|
|
|
|
call s:add_methods('repo',['prepare','git_command','git_chomp','git_chomp_in_tree','rev_parse'])
|
|
|
|
function! s:repo_superglob(base) dict abort
|
|
return map(fugitive#CompleteObject(a:base, self.git_dir), 'substitute(v:val, ''\\\(.\)'', ''\1'', "g")')
|
|
endfunction
|
|
|
|
call s:add_methods('repo',['superglob'])
|
|
|
|
function! s:repo_config(name) dict abort
|
|
return fugitive#Config(a:name, self.git_dir)
|
|
endfunction
|
|
|
|
function! s:repo_user() dict abort
|
|
let username = self.config('user.name')
|
|
let useremail = self.config('user.email')
|
|
return username.' <'.useremail.'>'
|
|
endfunction
|
|
|
|
call s:add_methods('repo',['config', 'user'])
|
|
|
|
" Section: File API
|
|
|
|
function! s:DirCommitFile(path) abort
|
|
let vals = matchlist(s:Slash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40\}\|[0-3]\)\(/.*\)\=$')
|
|
if empty(vals)
|
|
return ['', '', '']
|
|
endif
|
|
return vals[1:3]
|
|
endfunction
|
|
|
|
function! s:DirRev(url) abort
|
|
let [dir, commit, file] = s:DirCommitFile(a:url)
|
|
return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
|
|
endfunction
|
|
|
|
function! s:Owner(path, ...) abort
|
|
let dir = a:0 ? a:1 : s:Dir()
|
|
if empty(dir)
|
|
return ''
|
|
endif
|
|
let [pdir, commit, file] = s:DirCommitFile(a:path)
|
|
if s:cpath(dir, pdir)
|
|
if commit =~# '^\x\{40\}$'
|
|
return commit
|
|
elseif commit ==# '2'
|
|
return 'HEAD^{}'
|
|
endif
|
|
if filereadable(dir . '/MERGE_HEAD')
|
|
let merge_head = 'MERGE_HEAD'
|
|
elseif filereadable(dir . '/REBASE_HEAD')
|
|
let merge_head = 'REBASE_HEAD'
|
|
else
|
|
return ''
|
|
endif
|
|
if commit ==# '3'
|
|
return merge_head . '^{}'
|
|
elseif commit ==# '1'
|
|
return s:TreeChomp('merge-base', 'HEAD', merge_head, '--')
|
|
endif
|
|
endif
|
|
let path = fnamemodify(a:path, ':p')
|
|
if s:cpath(dir . '/', path[0 : len(dir)]) && a:path =~# 'HEAD$'
|
|
return strpart(path, len(dir) + 1)
|
|
endif
|
|
let refs = fugitive#CommonDir(dir) . '/refs'
|
|
if s:cpath(refs . '/', path[0 : len(refs)]) && path !~# '[\/]$'
|
|
return strpart(path, len(refs) - 4)
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
function! fugitive#Real(url) abort
|
|
if empty(a:url)
|
|
return ''
|
|
endif
|
|
let [dir, commit, file] = s:DirCommitFile(a:url)
|
|
if len(dir)
|
|
let tree = s:Tree(dir)
|
|
return s:PlatformSlash((len(tree) ? tree : dir) . file)
|
|
endif
|
|
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?[\/]$??'))
|
|
endif
|
|
return s:PlatformSlash(empty(url) ? a:url : url)
|
|
endfunction
|
|
|
|
function! fugitive#Path(url, ...) abort
|
|
if empty(a:url)
|
|
return ''
|
|
endif
|
|
let dir = a:0 > 1 ? a:2 : s:Dir()
|
|
let tree = s:Tree(dir)
|
|
if !a:0
|
|
return fugitive#Real(a:url)
|
|
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
|
|
endif
|
|
let url = a:url
|
|
if has_key(get(s:temp_files, s:cpath(url), {}), 'bufnr')
|
|
let url = bufname(s:temp_files[s:cpath(url)].bufnr)
|
|
endif
|
|
let url = s:Slash(fnamemodify(url, ':p'))
|
|
if url =~# '/$' && s:Slash(a:url) !~# '/$'
|
|
let url = url[0:-2]
|
|
endif
|
|
let [argdir, commit, file] = s:DirCommitFile(a:url)
|
|
if len(argdir) && s:cpath(argdir) !=# s:cpath(dir)
|
|
let file = ''
|
|
elseif len(dir) && s:cpath(url[0 : len(dir)]) ==# s:cpath(dir . '/')
|
|
let file = '/.git'.url[strlen(dir) : -1]
|
|
elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/')
|
|
let file = url[len(tree) : -1]
|
|
elseif s:cpath(url) ==# s:cpath(tree)
|
|
let file = '/'
|
|
endif
|
|
if empty(file) && a:1 =~# '^$\|^[.:]/$'
|
|
return s:Slash(fugitive#Real(a:url))
|
|
endif
|
|
return substitute(file, '^/', a:1, '')
|
|
endfunction
|
|
|
|
function! s:Relative(...) abort
|
|
return fugitive#Path(@%, a:0 ? a:1 : ':(top)')
|
|
endfunction
|
|
|
|
function! fugitive#Find(object, ...) abort
|
|
if type(a:object) == type(0)
|
|
let name = bufname(a:object)
|
|
return s:PlatformSlash(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name)
|
|
elseif a:object =~# '^[~$]'
|
|
let prefix = matchstr(a:object, '^[~$]\i*')
|
|
let owner = expand(prefix)
|
|
return s:PlatformSlash((len(owner) ? owner : prefix) . strpart(a:object, len(prefix)))
|
|
elseif s:Slash(a:object) =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
|
|
return s:PlatformSlash(a:object)
|
|
elseif s:Slash(a:object) =~# '^\.\.\=\%(/\|$\)'
|
|
return s:PlatformSlash(simplify(getcwd() . '/' . a:object))
|
|
endif
|
|
let dir = a:0 ? a:1 : s:Dir()
|
|
if empty(dir)
|
|
let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs.*', '', '')
|
|
let dir = FugitiveExtractGitDir(file)
|
|
if empty(dir)
|
|
return fnamemodify(len(file) ? file : a:object, ':p')
|
|
endif
|
|
endif
|
|
let rev = s:Slash(a:object)
|
|
let tree = s:Tree(dir)
|
|
let base = len(tree) ? tree : 'fugitive://' . dir . '//0'
|
|
if rev ==# '.git'
|
|
let f = len(tree) ? tree . '/.git' : dir
|
|
elseif rev =~# '^\.git/'
|
|
let f = substitute(rev, '^\.git', '', '')
|
|
let cdir = fugitive#CommonDir(dir)
|
|
if f =~# '^/\.\./\.\.\%(/\|$\)'
|
|
let f = simplify(len(tree) ? tree . f[3:-1] : dir . f)
|
|
elseif f =~# '^/\.\.\%(/\|$\)'
|
|
let f = base . f[3:-1]
|
|
elseif cdir !=# dir && (
|
|
\ f =~# '^/\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' ||
|
|
\ f !~# '^/logs$\|/\w*HEAD$' && getftime(dir . f) < 0 && getftime(cdir . f) >= 0)
|
|
let f = simplify(cdir . f)
|
|
else
|
|
let f = simplify(dir . f)
|
|
endif
|
|
elseif rev ==# ':/'
|
|
let f = base
|
|
elseif rev =~# '^\.\%(/\|$\)'
|
|
let f = base . rev[1:-1]
|
|
elseif rev =~# '^::\%(/\|\a\+\:\)'
|
|
let f = rev[2:-1]
|
|
elseif rev =~# '^::\.\.\=\%(/\|$\)'
|
|
let f = simplify(getcwd() . '/' . rev[2:-1])
|
|
elseif rev =~# '^::'
|
|
let f = base . '/' . rev[2:-1]
|
|
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)])
|
|
let f = 'fugitive://' . dir . '//' . +matchstr(rev, '^:\zs\d\ze:') . '/' . strpart(f, len(base) + 1)
|
|
else
|
|
let altdir = FugitiveExtractGitDir(f)
|
|
if len(altdir) && !s:cpath(dir, altdir)
|
|
return fugitive#Find(a:object, altdir)
|
|
endif
|
|
endif
|
|
elseif rev =~# '^:[0-3]:'
|
|
let f = 'fugitive://' . dir . '//' . rev[1] . '/' . rev[3:-1]
|
|
elseif rev ==# ':'
|
|
if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && s:cpath(fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(dir)]) ==# s:cpath(dir . '/') && filereadable($GIT_INDEX_FILE)
|
|
let f = fnamemodify($GIT_INDEX_FILE, ':p')
|
|
else
|
|
let f = dir . '/index'
|
|
endif
|
|
elseif rev =~# '^:(\%(top\|top,literal\|literal,top\|literal\))'
|
|
let f = base . '/' . matchstr(rev, ')\zs.*')
|
|
elseif rev =~# '^:/\@!'
|
|
let f = 'fugitive://' . dir . '//0/' . rev[1:-1]
|
|
else
|
|
if rev =~# 'HEAD$\|^refs/' && rev !~# ':'
|
|
let cdir = rev =~# '^refs/' ? fugitive#CommonDir(dir) : dir
|
|
if filereadable(cdir . '/' . rev)
|
|
let f = simplify(cdir . '/' . rev)
|
|
endif
|
|
endif
|
|
if !exists('f')
|
|
let commit = substitute(matchstr(rev, '^[^:]\+\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '')
|
|
let file = substitute(matchstr(rev, '^[^:]\+\zs:.*'), '^:', '/', '')
|
|
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)
|
|
return fugitive#Find(a:object, altdir)
|
|
endif
|
|
return file
|
|
endif
|
|
endif
|
|
if commit !~# '^[0-9a-f]\{40\}$'
|
|
let commit = system(s:Prepare(dir, 'rev-parse', '--verify', commit, '--'))[0:-2]
|
|
let commit = v:shell_error ? '' : commit
|
|
endif
|
|
if len(commit)
|
|
let f = 'fugitive://' . dir . '//' . commit . file
|
|
else
|
|
let f = base . '/' . substitute(rev, '^:/:\=\|^[^:]\+:', '', '')
|
|
endif
|
|
endif
|
|
endif
|
|
return s:PlatformSlash(f)
|
|
endfunction
|
|
|
|
function! s:Generate(rev, ...) abort
|
|
let dir = a:0 ? a:1 : s:Dir()
|
|
let tree = s:Tree(dir)
|
|
let object = a:rev
|
|
if a:rev =~# '^/\.git\%(/\|$\)'
|
|
let object = a:rev[1:-1]
|
|
elseif a:rev =~# '^/' && len(tree) && getftime(tree . a:rev) >= 0 && getftime(a:rev) < 0
|
|
let object = ':(top)' . a:rev[1:-1]
|
|
endif
|
|
return fugitive#Find(object, dir)
|
|
endfunction
|
|
|
|
function! s:DotRelative(path) abort
|
|
let cwd = getcwd()
|
|
let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
|
|
if s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
|
|
return '.' . strpart(path, len(cwd))
|
|
endif
|
|
return a:path
|
|
endfunction
|
|
|
|
function! fugitive#Object(...) abort
|
|
let dir = a:0 > 1 ? a:2 : s:Dir()
|
|
let [fdir, rev] = s:DirRev(a:0 ? a:1 : @%)
|
|
if s:cpath(dir) !=# s:cpath(fdir)
|
|
let rev = ''
|
|
endif
|
|
let tree = s:Tree(dir)
|
|
if empty(rev) && empty(tree)
|
|
elseif empty(rev)
|
|
let rev = fugitive#Path(a:0 ? a:1 : @%, './', dir)
|
|
let cdir = fugitive#CommonDir(dir)
|
|
if rev =~# '^\./\.git/refs/\%(tags\|heads\|remotes\)/.\|^\./\.git/\w*HEAD$'
|
|
let rev = rev[7:-1]
|
|
elseif s:cpath(cdir . '/refs/', rev[0 : len(cdir)])
|
|
let rev = strpart(rev, len(cdir)+1)
|
|
elseif rev =~# '^\./.git\%(/\|$\)'
|
|
return fnamemodify(a:0 ? a:1 : @%, ':p')
|
|
endif
|
|
endif
|
|
if rev !~# '^\.\%(/\|$\)' || s:cpath(getcwd(), tree)
|
|
return rev
|
|
else
|
|
return tree . rev[1:-1]
|
|
endif
|
|
endfunction
|
|
|
|
let s:var = '\%(%\|#<\=\d\+\|##\=\)'
|
|
let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
|
|
let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
|
|
|
|
function! s:BufName(var) abort
|
|
if a:var ==# '%'
|
|
return bufname(get(b:, 'fugitive_blamed_bufnr', ''))
|
|
elseif a:var =~# '^#\d*$'
|
|
let nr = getbufvar(+a:var[1:-1], 'fugitive_blamed_bufnr', '')
|
|
return bufname(nr ? nr : +a:var[1:-1])
|
|
else
|
|
return expand(a:var)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:ExpandVar(other, var, flags, esc) abort
|
|
if a:other =~# '^\'
|
|
return a:other[1:-1]
|
|
elseif a:other =~# '^!'
|
|
let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
|
|
let owner = s:Owner(buffer)
|
|
return len(owner) ? owner : '@'
|
|
endif
|
|
let flags = a:flags
|
|
let file = s:DotRelative(fugitive#Real(s:BufName(a:var)))
|
|
while len(flags)
|
|
let flag = matchstr(flags, s:flag)
|
|
let flags = strpart(flags, len(flag))
|
|
if flag ==# ':.'
|
|
let file = s:DotRelative(file)
|
|
else
|
|
let file = fnamemodify(file, flag)
|
|
endif
|
|
endwhile
|
|
let file = s:Slash(file)
|
|
return (len(a:esc) ? shellescape(file) : file)
|
|
endfunction
|
|
|
|
function! s:Expand(rev) abort
|
|
if a:rev =~# '^:[0-3]$'
|
|
let file = a:rev . s:Relative(':')
|
|
elseif a:rev =~# '^-'
|
|
let file = 'HEAD^{}' . a:rev[1:-1] . s:Relative(':')
|
|
elseif a:rev =~# '^@{'
|
|
let file = 'HEAD' . a:rev. s:Relative(':')
|
|
elseif a:rev =~# '^\^[0-9~^{]\|^\~[0-9~^]'
|
|
let commit = substitute(s:DirCommitFile(@%)[1], '^\d\=$', 'HEAD', '')
|
|
let file = commit . a:rev . s:Relative(':')
|
|
else
|
|
let file = a:rev
|
|
endif
|
|
return substitute(file,
|
|
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
|
|
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),"")', 'g')
|
|
endfunction
|
|
|
|
function! fugitive#Expand(object) abort
|
|
return substitute(a:object,
|
|
\ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
|
|
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
|
|
endfunction
|
|
|
|
function! s:ShellExpand(cmd) abort
|
|
return substitute(a:cmd, '\(\\[!#%]\|!\d*\)\|' . s:expand,
|
|
\ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
|
|
endfunction
|
|
|
|
let s:trees = {}
|
|
let s:indexes = {}
|
|
function! s:TreeInfo(dir, commit) abort
|
|
if a:commit =~# '^:\=[0-3]$'
|
|
let index = get(s:indexes, a:dir, [])
|
|
let newftime = getftime(a:dir . '/index')
|
|
if get(index, 0, -1) < newftime
|
|
let out = system(fugitive#Prepare(a:dir, 'ls-files', '--stage', '--'))
|
|
let s:indexes[a:dir] = [newftime, {'0': {}, '1': {}, '2': {}, '3': {}}]
|
|
if v:shell_error
|
|
return [{}, -1]
|
|
endif
|
|
for line in split(out, "\n")
|
|
let [info, filename] = split(line, "\t")
|
|
let [mode, sha, stage] = split(info, '\s\+')
|
|
let s:indexes[a:dir][1][stage][filename] = [newftime, mode, 'blob', sha, -2]
|
|
while filename =~# '/'
|
|
let filename = substitute(filename, '/[^/]*$', '', '')
|
|
let s:indexes[a:dir][1][stage][filename] = [newftime, '040000', 'tree', '', 0]
|
|
endwhile
|
|
endfor
|
|
endif
|
|
return [get(s:indexes[a:dir][1], a:commit[-1:-1], {}), newftime]
|
|
elseif a:commit =~# '^\x\{40\}$'
|
|
if !has_key(s:trees, a:dir)
|
|
let s:trees[a:dir] = {}
|
|
endif
|
|
if !has_key(s:trees[a:dir], a:commit)
|
|
let ftime = +system(fugitive#Prepare(a:dir, 'log', '-1', '--pretty=format:%ct', a:commit, '--'))
|
|
if v:shell_error
|
|
let s:trees[a:dir][a:commit] = [{}, -1]
|
|
return s:trees[a:dir][a:commit]
|
|
endif
|
|
let s:trees[a:dir][a:commit] = [{}, +ftime]
|
|
let out = system(fugitive#Prepare(a:dir, 'ls-tree', '-rtl', '--full-name', a:commit, '--'))
|
|
if v:shell_error
|
|
return s:trees[a:dir][a:commit]
|
|
endif
|
|
for line in split(out, "\n")
|
|
let [info, filename] = split(line, "\t")
|
|
let [mode, type, sha, size] = split(info, '\s\+')
|
|
let s:trees[a:dir][a:commit][0][filename] = [ftime, mode, type, sha, +size, filename]
|
|
endfor
|
|
endif
|
|
return s:trees[a:dir][a:commit]
|
|
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, [])
|
|
if empty(entry) || file =~# '/$' && entry[1] !=# 'tree'
|
|
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 ''
|
|
endif
|
|
if file =~# '/\.\.\%(/\|$\)'
|
|
let tree = s:Tree(dir)
|
|
if len(tree)
|
|
let path = simplify(tree . file)
|
|
if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/'
|
|
return s:PlatformSlash(path)
|
|
endif
|
|
endif
|
|
endif
|
|
return s:PlatformSlash('fugitive://' . simplify(dir) . '//' . commit . simplify(file))
|
|
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]
|
|
let size = +system(s:Prepare(dir, 'cat-file', '-s', entry[3]))
|
|
let entry[4] = v:shell_error ? -1 : size
|
|
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
|
|
|
|
function! fugitive#filewritable(url) abort
|
|
let [dir, commit, file] = s:DirCommitFile(a:url)
|
|
if commit !~# '^\d$' || !filewritable(dir . '/index')
|
|
return 0
|
|
endif
|
|
return s:PathInfo(a:url)[2] ==# 'blob' ? 1 : 2
|
|
endfunction
|
|
|
|
function! fugitive#isdirectory(url) abort
|
|
return s:PathInfo(a:url)[2] ==# 'tree'
|
|
endfunction
|
|
|
|
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
|
|
|
|
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
|
|
call system(s:Prepare(dir, 'update-index', '--index-info'),
|
|
\ (a:perm =~# 'x' ? '000755 ' : '000644 ') . entry[3] . ' ' . commit . "\t" . file[1:-1])
|
|
return v:shell_error ? -1 : 0
|
|
endfunction
|
|
|
|
function! s:TempCmd(out, cmd) abort
|
|
let prefix = ''
|
|
try
|
|
let cmd = (type(a:cmd) == type([]) ? call('s:Prepare', a:cmd) : a:cmd)
|
|
let redir = ' > ' . a:out
|
|
if s:winshell()
|
|
let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
|
|
return s:System('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"')
|
|
elseif &shell =~# 'fish'
|
|
return s:System(' begin;' . prefix . cmd . redir . ';end ')
|
|
else
|
|
return s:System(' (' . prefix . cmd . redir . ') ')
|
|
endif
|
|
endtry
|
|
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
|
|
if !has_key(s:blobdirs, dir)
|
|
let s:blobdirs[dir] = tempname()
|
|
endif
|
|
let tempfile = s:blobdirs[dir] . '/' . commit . file
|
|
let tempparent = fnamemodify(tempfile, ':h')
|
|
if !isdirectory(tempparent)
|
|
call mkdir(tempparent, 'p')
|
|
endif
|
|
if commit =~# '^\d$' || !filereadable(tempfile)
|
|
let rev = s:DirRev(a:url)[1]
|
|
let command = s:Prepare(dir, 'cat-file', 'blob', rev)
|
|
call s:TempCmd(tempfile, command)
|
|
if v:shell_error
|
|
call delete(tempfile)
|
|
return ''
|
|
endif
|
|
endif
|
|
return s:Resolve(tempfile)
|
|
endfunction
|
|
|
|
function! fugitive#readfile(url, ...) abort
|
|
let entry = s:PathInfo(a:url)
|
|
if entry[2] !=# 'blob'
|
|
return []
|
|
endif
|
|
let temp = s:BlobTemp(a:url)
|
|
if empty(temp)
|
|
return []
|
|
endif
|
|
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'
|
|
let temp = tempname()
|
|
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)
|
|
let hash = system(s:Prepare(dir, 'hash-object', '-w', temp))[0:-2]
|
|
let mode = len(entry[1]) ? entry[1] : '100644'
|
|
if !v:shell_error && hash =~# '^\x\{40\}$'
|
|
call system(s:Prepare(dir, 'update-index', '--index-info'),
|
|
\ mode . ' ' . hash . ' ' . commit . "\t" . file[1:-1])
|
|
if !v:shell_error
|
|
return 0
|
|
endif
|
|
endif
|
|
endif
|
|
return call('writefile', [a:lines, a:url] + a:000)
|
|
endfunction
|
|
|
|
let s:globsubs = {
|
|
\ '/**/': '/\%([^./][^/]*/\)*',
|
|
\ '/**': '/\%([^./][^/]\+/\)*[^./][^/]*',
|
|
\ '**/': '[^/]*\%(/[^./][^/]*\)*',
|
|
\ '**': '.*',
|
|
\ '/*': '/[^/.][^/]*',
|
|
\ '*': '[^/]*',
|
|
\ '?': '[^/]'}
|
|
function! fugitive#glob(url, ...) abort
|
|
let [dirglob, commit, glob] = s:DirCommitFile(a:url)
|
|
let append = matchstr(glob, '/*$')
|
|
let glob = substitute(glob, '/*$', '', '')
|
|
let pattern = '^' . substitute(glob, '/\=\*\*/\=\|/\=\*\|[.?\^$]', '\=get(s:globsubs, submatch(0), "\\" . submatch(0))', 'g')[1:-1] . '$'
|
|
let results = []
|
|
for dir in dirglob =~# '[*?]' ? split(glob(dirglob), "\n") : [dirglob]
|
|
if empty(dir) || !get(g:, 'fugitive_file_api', 1) || !filereadable(dir . '/HEAD')
|
|
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')
|
|
let prepend = 'fugitive://' . dir . '//' . substitute(commit, '^:', '', '') . '/'
|
|
call sort(files)
|
|
call map(files, 's:PlatformSlash(prepend . v:val . append)')
|
|
call extend(results, files)
|
|
endfor
|
|
if a:0 > 1 && a:2
|
|
return results
|
|
else
|
|
return join(results, "\n")
|
|
endif
|
|
endfunction
|
|
|
|
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
|
|
call system(s:Prepare(dir, 'update-index', '--index-info'),
|
|
\ '000000 0000000000000000000000000000000000000000 ' . commit . "\t" . file[1:-1])
|
|
return v:shell_error ? -1 : 0
|
|
endfunction
|
|
|
|
" Section: Buffer Object
|
|
|
|
let s:buffer_prototype = {}
|
|
|
|
function! fugitive#buffer(...) abort
|
|
let buffer = {'#': bufnr(a:0 ? a:1 : '%')}
|
|
call extend(buffer, s:buffer_prototype, 'keep')
|
|
return buffer
|
|
endfunction
|
|
|
|
function! s:buffer_getvar(var) dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().getvar() which has been removed. Replace it with the local variable or getbufvar()"
|
|
endfunction
|
|
|
|
function! s:buffer_getline(lnum) dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().getline() which has been removed. Replace it with getline() or getbufline()"
|
|
endfunction
|
|
|
|
function! s:buffer_repo() dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().repo() which has been removed. Replace it with fugitive#repo()"
|
|
endfunction
|
|
|
|
function! s:buffer_type(...) dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().type() which has been removed. Replace it with get(b:, 'fugitive_type', '')"
|
|
endfunction
|
|
|
|
function! s:buffer_spec() dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().spec() which has been removed. Replace it with bufname(), expand('%:p'), etc"
|
|
endfunction
|
|
|
|
function! s:buffer_name() dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().name() which has been removed. Replace it with bufname(), expand('%:p'), etc"
|
|
endfunction
|
|
|
|
function! s:buffer_commit() dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().commit() which has been removed. Replace it with matchstr(FugitiveParse()[0], '^\x\+')"
|
|
endfunction
|
|
|
|
function! s:buffer_relative(...) dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().relative() which has been removed. Replace it with FugitivePath(@%, " . string(a:0 ? a:1 : '') . ")"
|
|
endfunction
|
|
|
|
function! s:buffer_path(...) dict abort
|
|
throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().path() which has been removed. Replace it with FugitivePath(@%, " . string(a:0 ? a:1 : '') . ")"
|
|
endfunction
|
|
|
|
call s:add_methods('buffer',['getvar','getline','repo','type','spec','name','commit','path','relative'])
|
|
|
|
" Section: Completion
|
|
|
|
function! s:GlobComplete(lead, pattern) abort
|
|
if v:version >= 704
|
|
let results = glob(a:lead . a:pattern, 0, 1)
|
|
else
|
|
let results = split(glob(a:lead . a:pattern), "\n")
|
|
endif
|
|
call map(results, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val')
|
|
call map(results, 'v:val[ strlen(a:lead) : -1 ]')
|
|
return results
|
|
endfunction
|
|
|
|
function! fugitive#CompletePath(base, ...) abort
|
|
let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
|
|
let tree = s:Tree(dir) . '/'
|
|
let strip = '^\%(:/:\=\|:(top)\|:(top,literal)\|:(literal,top)\|:(literal)\)'
|
|
let base = substitute(a:base, strip, '', '')
|
|
if base =~# '^\.git/'
|
|
let pattern = s:gsub(base[5:-1], '/', '*&').'*'
|
|
let matches = s:GlobComplete(dir . '/', pattern)
|
|
let cdir = fugitive#CommonDir(dir)
|
|
if len(cdir) && s:cpath(dir) !=# s:cpath(cdir)
|
|
call extend(matches, s:GlobComplete(cdir . '/', pattern))
|
|
endif
|
|
call s:Uniq(matches)
|
|
call map(matches, "'.git/' . v:val")
|
|
elseif base =~# '^\~/'
|
|
let matches = map(s:GlobComplete(expand('~/'), base[2:-1] . '*'), '"~/" . v:val')
|
|
elseif a:base =~# '^/\|^\a\+:\|^\.\.\=/\|^:(literal)'
|
|
let matches = s:GlobComplete('', base . '*')
|
|
elseif len(tree) > 1
|
|
let matches = s:GlobComplete(tree, s:gsub(base, '/', '*&').'*')
|
|
else
|
|
let matches = []
|
|
endif
|
|
call map(matches, 's:fnameescape(s:Slash(matchstr(a:base, strip) . v:val))')
|
|
return matches
|
|
endfunction
|
|
|
|
function! fugitive#PathComplete(...) abort
|
|
return call('fugitive#CompletePath', a:000)
|
|
endfunction
|
|
|
|
function! fugitive#CompleteObject(base, ...) abort
|
|
let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
|
|
let cwd = a:0 == 1 ? s:Tree(dir) : getcwd()
|
|
let tree = s:Tree(dir) . '/'
|
|
let subdir = ''
|
|
if len(tree) > 1 && s:cpath(tree, cwd[0 : len(tree) - 1])
|
|
let subdir = strpart(cwd, len(tree)) . '/'
|
|
endif
|
|
|
|
if a:base =~# '^\.\=/\|^:(' || a:base !~# ':'
|
|
let results = []
|
|
if a:base =~# '^refs/'
|
|
let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)')
|
|
elseif a:base !~# '^\.\=/\|^:('
|
|
let heads = ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'refs/']
|
|
let heads += sort(split(s:TreeChomp(["rev-parse","--symbolic","--branches","--tags","--remotes"], dir),"\n"))
|
|
if filereadable(fugitive#CommonDir(dir) . '/refs/stash')
|
|
let heads += ["stash"]
|
|
let heads += sort(split(s:TreeChomp(["stash","list","--pretty=format:%gd"], dir),"\n"))
|
|
endif
|
|
call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
|
|
let results += heads
|
|
endif
|
|
call map(results, 's:fnameescape(v:val)')
|
|
if !empty(tree)
|
|
let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base)
|
|
endif
|
|
return results
|
|
|
|
elseif a:base =~# '^:'
|
|
let entries = split(s:TreeChomp(['ls-files','--stage'], dir),"\n")
|
|
if a:base =~# ':\./'
|
|
call map(entries, 'substitute(v:val, "\\M\t\\zs" . subdir, "./", "")')
|
|
endif
|
|
call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
|
|
if a:base !~# '^:[0-3]\%(:\|$\)'
|
|
call filter(entries,'v:val[1] == "0"')
|
|
call map(entries,'v:val[2:-1]')
|
|
endif
|
|
|
|
else
|
|
let tree = matchstr(a:base, '.*[:/]')
|
|
let entries = split(s:TreeChomp(['ls-tree', substitute(tree, ':\zs\./', '\=subdir', '')], dir),"\n")
|
|
call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
|
|
call map(entries,'tree.s:sub(v:val,".*\t","")')
|
|
|
|
endif
|
|
call filter(entries, 'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
|
|
return map(entries, 's:fnameescape(v:val)')
|
|
endfunction
|
|
|
|
function! fugitive#Complete(...) abort
|
|
return call('fugitive#CompleteObject', a:000)
|
|
endfunction
|
|
|
|
" Section: Buffer auto-commands
|
|
|
|
function! s:ReplaceCmd(cmd) abort
|
|
let temp = tempname()
|
|
let err = s:TempCmd(temp, a:cmd)
|
|
if v:shell_error
|
|
call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
|
|
endif
|
|
let temp = s:Resolve(temp)
|
|
let fn = expand('%:p')
|
|
silent exe 'keepalt file '.temp
|
|
let modelines = &modelines
|
|
try
|
|
set modelines=0
|
|
silent keepjumps noautocmd edit!
|
|
finally
|
|
let &modelines = modelines
|
|
try
|
|
silent exe 'keepalt file '.s:fnameescape(fn)
|
|
catch /^Vim\%((\a\+)\)\=:E302:/
|
|
endtry
|
|
call delete(temp)
|
|
if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
|
|
silent execute 'bwipeout '.bufnr('$')
|
|
endif
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:QueryLog(refspec) abort
|
|
let lines = split(system(FugitivePrepare('log', '-n', '256', '--format=%h%x09%s', a:refspec, '--')), "\n")
|
|
if v:shell_error
|
|
return []
|
|
endif
|
|
call map(lines, 'split(v:val, "\t")')
|
|
call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
|
|
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
|
|
silent 2delete _
|
|
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
|
|
|
|
function! fugitive#BufReadStatus() abort
|
|
let amatch = s:Slash(expand('%:p'))
|
|
let b:fugitive_type = 'index'
|
|
try
|
|
silent doautocmd BufReadPre
|
|
let cmd = [fnamemodify(amatch, ':h')]
|
|
setlocal noro ma nomodeline buftype=nowrite
|
|
if s:cpath(fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : s:Dir() . '/index', ':p')) !=# s:cpath(amatch)
|
|
let cmd += ['-c', 'GIT_INDEX_FILE=' . amatch]
|
|
endif
|
|
let cmd += ['status', '--porcelain', '-bz']
|
|
let output = split(system(fugitive#Prepare(cmd)), "\1")
|
|
if v:shell_error
|
|
throw 'fugitive: ' . join(output, ' ')
|
|
endif
|
|
|
|
let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
|
|
let pull = ''
|
|
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
|
|
endif
|
|
|
|
let b:fugitive_status = {'Staged': {}, 'Unstaged': {}}
|
|
let [staged, unstaged, untracked] = [[], [], []]
|
|
let i = 0
|
|
while i < len(output)
|
|
let line = output[i]
|
|
let file = line[3:-1]
|
|
let files = file
|
|
let i += 1
|
|
if line[2] !=# ' '
|
|
continue
|
|
endif
|
|
if line[0:1] =~# '[RC]'
|
|
let files = output[i] . ' -> ' . file
|
|
let i += 1
|
|
endif
|
|
if line[0] !~# '[ ?!#]'
|
|
call add(staged, {'type': 'File', 'status': line[0], 'filename': files})
|
|
let b:fugitive_status['Staged'][files] = line[0]
|
|
endif
|
|
if line[1] =~# '?'
|
|
call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
|
|
let b:fugitive_status['Unstaged'][files] = line[1]
|
|
elseif line[1] !~# '[ !#]'
|
|
call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files})
|
|
let b:fugitive_status['Unstaged'][files] = line[1]
|
|
endif
|
|
endwhile
|
|
let unstaged = extend(untracked, unstaged)
|
|
|
|
for dict in staged
|
|
let b:fugitive_status['Staged'][dict.filename] = dict.status
|
|
endfor
|
|
for dict in unstaged
|
|
let b:fugitive_status['Unstaged'][dict.filename] = dict.status
|
|
endfor
|
|
|
|
let config = fugitive#Config()
|
|
|
|
let pull_type = 'Pull'
|
|
if len(pull)
|
|
let rebase = fugitive#Config('branch.' . branch . '.rebase', config)
|
|
if empty(rebase)
|
|
let rebase = fugitive#Config('pull.rebase', config)
|
|
endif
|
|
if rebase =~# '^\%(true\|yes\|on\|1\|interactive\)$'
|
|
let pull_type = 'Rebase'
|
|
elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
|
|
let pull_type = 'Merge'
|
|
endif
|
|
endif
|
|
|
|
let push_remote = fugitive#Config('branch.' . branch . '.pushRemote', config)
|
|
if empty(push_remote)
|
|
let push_remote = fugitive#Config('remote.pushDefault', config)
|
|
endif
|
|
let push = len(push_remote) && len(branch) ? push_remote . '/' . branch : ''
|
|
if empty(push)
|
|
let push = pull
|
|
endif
|
|
|
|
if len(pull)
|
|
let unpulled = s:QueryLog(head . '..' . pull)
|
|
else
|
|
let unpulled = []
|
|
endif
|
|
if len(push)
|
|
let unpushed = s:QueryLog(push . '..' . head)
|
|
else
|
|
let unpushed = []
|
|
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
|
|
endif
|
|
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
|
|
endif
|
|
|
|
let b:fugitive_diff = {
|
|
\ 'Staged': split(system(fugitive#Prepare('diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached')), "\n"),
|
|
\ 'Unstaged': split(system(fugitive#Prepare('diff', '--color=never', '--no-ext-diff', '--no-prefix')), "\n")}
|
|
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
|
|
call s:AddSection('Rebasing ' . rebasing_head, rebasing)
|
|
call s:AddSection('Unstaged', unstaged)
|
|
let unstaged_end = len(unstaged) ? line('$') : 0
|
|
call s:AddSection('Staged', staged)
|
|
let staged_end = len(staged) ? line('$') : 0
|
|
call s:AddSection('Unpushed to ' . push, unpushed)
|
|
call s:AddSection('Unpulled from ' . pull, unpulled)
|
|
|
|
setlocal nomodified readonly noswapfile
|
|
silent doautocmd BufReadPost
|
|
setlocal nomodifiable
|
|
if &bufhidden ==# ''
|
|
setlocal bufhidden=delete
|
|
endif
|
|
let b:dispatch = ':Gfetch --all'
|
|
call fugitive#MapJumps()
|
|
let nowait = v:version >= 704 ? '<nowait>' : ''
|
|
nunmap <buffer> P
|
|
nunmap <buffer> ~
|
|
nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>StageNext(v:count1)<CR>
|
|
nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>StagePrevious(v:count1)<CR>
|
|
exe "nnoremap <buffer> <silent>" nowait "- :<C-U>execute <SID>Do('Toggle',0)<CR>"
|
|
exe "xnoremap <buffer> <silent>" nowait "- :<C-U>execute <SID>Do('Toggle',1)<CR>"
|
|
exe "nnoremap <buffer> <silent>" nowait "s :<C-U>execute <SID>Do('Stage',0)<CR>"
|
|
exe "xnoremap <buffer> <silent>" nowait "s :<C-U>execute <SID>Do('Stage',1)<CR>"
|
|
exe "nnoremap <buffer> <silent>" nowait "u :<C-U>execute <SID>Do('Unstage',0)<CR>"
|
|
exe "xnoremap <buffer> <silent>" nowait "u :<C-U>execute <SID>Do('Unstage',1)<CR>"
|
|
nnoremap <buffer> <silent> C :<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR>
|
|
nnoremap <buffer> <silent> a :<C-U>execute <SID>Do('Toggle',0)<CR>
|
|
nnoremap <buffer> <silent> i :<C-U>execute <SID>StageIntend(v:count1)<CR>
|
|
exe 'nnoremap <buffer> <silent>' nowait "= :<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>"
|
|
exe 'nnoremap <buffer> <silent>' nowait "< :<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>"
|
|
exe 'nnoremap <buffer> <silent>' nowait "> :<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>"
|
|
exe 'xnoremap <buffer> <silent>' nowait "= :<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>"
|
|
exe 'xnoremap <buffer> <silent>' nowait "< :<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>"
|
|
exe 'xnoremap <buffer> <silent>' nowait "> :<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>"
|
|
nnoremap <buffer> <silent> D :<C-U>execute <SID>StageDiff('Gdiff')<CR>
|
|
nnoremap <buffer> <silent> dd :<C-U>execute <SID>StageDiff('Gdiff')<CR>
|
|
nnoremap <buffer> <silent> dh :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
|
|
nnoremap <buffer> <silent> ds :<C-U>execute <SID>StageDiff('Gsdiff')<CR>
|
|
nnoremap <buffer> <silent> dp :<C-U>execute <SID>StageDiffEdit()<CR>
|
|
nnoremap <buffer> <silent> dv :<C-U>execute <SID>StageDiff('Gvdiff')<CR>
|
|
nnoremap <buffer> <silent> J :<C-U>execute <SID>StageNext(v:count1)<CR>
|
|
nnoremap <buffer> <silent> K :<C-U>execute <SID>StagePrevious(v:count1)<CR>
|
|
nnoremap <buffer> <silent> P :<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>
|
|
xnoremap <buffer> <silent> P :<C-U>execute <SID>StagePatch(line("'<"),line("'>"))<CR>
|
|
nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>
|
|
exe 'nnoremap <buffer> <silent>' nowait "gq :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>"
|
|
nnoremap <buffer> <silent> R :<C-U>exe <SID>ReloadStatus()<CR>
|
|
nnoremap <buffer> <silent> U :<C-U>echoerr 'Changed to X'<CR>
|
|
nnoremap <buffer> <silent> g<Bar> :<C-U>execute <SID>StageDelete(line('.'),v:count)<CR>
|
|
xnoremap <buffer> <silent> g<Bar> :<C-U>execute <SID>StageDelete(line("'<"),line("'>")-line("'<")+1)<CR>
|
|
nnoremap <buffer> <silent> X :<C-U>execute <SID>StageDelete(line('.'),v:count)<CR>
|
|
xnoremap <buffer> <silent> X :<C-U>execute <SID>StageDelete(line("'<"),line("'>")-line("'<")+1)<CR>
|
|
nnoremap <buffer> . :<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>
|
|
xnoremap <buffer> . :<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>
|
|
nnoremap <buffer> <silent> <F1> :help fugitive-mappings<CR>
|
|
setlocal filetype=fugitive
|
|
|
|
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
|
|
|
|
return ''
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
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
|
|
if rev !~# ':'
|
|
let cmd = s:Prepare(dir, 'log', '--pretty=format:%B', '-1', rev, '--')
|
|
else
|
|
let cmd = s:Prepare(dir, 'cat-file', '-p', rev)
|
|
endif
|
|
return line . 'read !' . escape(cmd, '!#%')
|
|
endfunction
|
|
|
|
function! fugitive#FileWriteCmd(...) abort
|
|
let tmp = tempname()
|
|
let amatch = a:0 ? a:1 : expand('<amatch>')
|
|
let autype = a:0 > 1 ? 'Buf' : 'File'
|
|
if exists('#' . autype . 'WritePre')
|
|
execute 'doautocmd ' . autype . 'WritePre ' . s:fnameescape(amatch)
|
|
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
|
|
silent execute "'[,']write !".s:Prepare(dir, 'hash-object', '-w', '--stdin', '--').' > '.tmp
|
|
let sha1 = readfile(tmp)[0]
|
|
let old_mode = matchstr(system(s:Prepare(dir, 'ls-files', '--stage', '.' . file)), '^\d\+')
|
|
if empty(old_mode)
|
|
let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
|
|
endif
|
|
let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1]
|
|
let error = system(s:Prepare(dir, 'update-index', '--index-info'), info . "\n")
|
|
if v:shell_error == 0
|
|
setlocal nomodified
|
|
if exists('#' . autype . 'WritePost')
|
|
execute 'doautocmd ' . autype . 'WritePost ' . s:fnameescape(amatch)
|
|
endif
|
|
call fugitive#ReloadStatus()
|
|
return ''
|
|
else
|
|
return 'echoerr '.string('fugitive: '.error)
|
|
endif
|
|
finally
|
|
call delete(tmp)
|
|
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
|
|
if rev =~# '^:\d$'
|
|
let b:fugitive_type = 'stage'
|
|
else
|
|
let b:fugitive_type = system(s:Prepare(dir, 'cat-file', '-t', rev))[0:-2]
|
|
if v:shell_error && rev =~# '^:0'
|
|
let sha = system(s:Prepare(dir, 'write-tree', '--prefix=' . rev[3:-1]))[0:-2]
|
|
let b:fugitive_type = 'tree'
|
|
endif
|
|
if v:shell_error
|
|
let error = b:fugitive_type
|
|
unlet b:fugitive_type
|
|
if rev =~# '^:\d:'
|
|
let &l:readonly = !filewritable(dir . '/index')
|
|
return 'silent doautocmd BufNewFile'
|
|
else
|
|
setlocal readonly nomodifiable
|
|
return 'echo ' . string(error)
|
|
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
|
|
|
|
try
|
|
silent doautocmd BufReadPre
|
|
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')
|
|
let sha = system(s:Prepare(dir, 'rev-parse', '--verify', rev, '--'))[0:-2]
|
|
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
|
|
call s:ReplaceCmd([dir, 'show', '--no-color', '--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])
|
|
keepjumps call search('^parent ')
|
|
if getline('.') ==# 'parent '
|
|
silent keepjumps delete_
|
|
else
|
|
silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
|
|
endif
|
|
keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
|
|
if lnum
|
|
silent keepjumps delete_
|
|
end
|
|
silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
|
|
keepjumps 1
|
|
endif
|
|
elseif b:fugitive_type ==# 'stage'
|
|
call s:ReplaceCmd([dir, 'ls-files', '--stage'])
|
|
elseif b:fugitive_type ==# 'blob'
|
|
call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
|
|
endif
|
|
finally
|
|
keepjumps call setpos('.',pos)
|
|
setlocal nomodified noswapfile
|
|
let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
|
|
let &l:readonly = !modifiable || !filewritable(dir . '/index')
|
|
if &bufhidden ==# ''
|
|
setlocal bufhidden=delete
|
|
endif
|
|
let &l:modifiable = modifiable
|
|
if b:fugitive_type !=# 'blob'
|
|
setlocal filetype=git foldmethod=syntax
|
|
nnoremap <buffer> <silent> a :<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>
|
|
nnoremap <buffer> <silent> i :<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>
|
|
endif
|
|
call fugitive#MapJumps()
|
|
endtry
|
|
|
|
setlocal modifiable
|
|
return 'silent doautocmd' . (v:version >= 704 ? ' <nomodeline>' : '') .
|
|
\ ' BufReadPost' . (modifiable ? '' : '|setl nomodifiable')
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
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
|
|
|
|
function! s:SetupTemp(file) abort
|
|
if has_key(s:temp_files, s:cpath(a:file))
|
|
let dict = s:temp_files[s:cpath(a:file)]
|
|
let b:git_dir = dict.dir
|
|
call extend(b:, {'fugitive_type': 'temp'}, 'keep')
|
|
if has_key(dict, 'filetype') && dict.filetype !=# &l:filetype
|
|
let &l:filetype = dict.filetype
|
|
endif
|
|
setlocal foldmarker=<<<<<<<,>>>>>>>
|
|
setlocal bufhidden=delete nobuflisted
|
|
setlocal buftype=nowrite
|
|
nnoremap <buffer> <silent> q :<C-U>bdelete<CR>
|
|
if getline(1) !~# '^diff '
|
|
setlocal nomodifiable
|
|
endif
|
|
call FugitiveDetect(a:file)
|
|
if &filetype ==# 'git'
|
|
call fugitive#MapJumps()
|
|
endif
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
augroup fugitive_temp
|
|
autocmd!
|
|
autocmd BufNewFile,BufReadPost * exe s:SetupTemp(expand('<amatch>:p'))
|
|
augroup END
|
|
|
|
" Section: :Git
|
|
|
|
call s:command("-bang -nargs=? -complete=customlist,fugitive#CompleteGit Git", "Git")
|
|
|
|
function! s:GitCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
|
|
if a:bang
|
|
return s:Open('edit', 1, a:mods, a:arg, a:args)
|
|
endif
|
|
let git = s:UserCommand()
|
|
if has('gui_running') && !has('win32')
|
|
let git .= ' --no-pager'
|
|
endif
|
|
let args = matchstr(a:arg,'\v\C.{-}%($|\\@<!%(\\\\)*\|)@=')
|
|
let after = matchstr(a:arg, '\v\C\\@<!%(\\\\)*\zs\|.*')
|
|
let tree = s:Tree()
|
|
if !s:CanAutoReloadStatus()
|
|
let after = '|call fugitive#ReloadStatus()' . after
|
|
endif
|
|
if exists(':terminal') && has('nvim') && !get(g:, 'fugitive_force_bang_command')
|
|
if len(@%)
|
|
-tabedit %
|
|
else
|
|
-tabnew
|
|
endif
|
|
execute 'lcd' fnameescape(tree)
|
|
let exec = escape(git . ' ' . s:ShellExpand(args), '#%')
|
|
return 'exe ' . string('terminal ' . exec) . after
|
|
else
|
|
let cmd = "exe '!'.escape(" . string(git) . " . ' ' . s:ShellExpand(" . string(args) . "),'!#%')"
|
|
if s:cpath(tree) !=# s:cpath(getcwd())
|
|
let cd = s:Cd()
|
|
let cmd = 'try|' . cd . ' ' . tree . '|' . cmd . '|finally|' . cd . ' ' . s:fnameescape(getcwd()) . '|endtry'
|
|
endif
|
|
return cmd . after
|
|
endif
|
|
endfunction
|
|
|
|
let s:exec_paths = {}
|
|
function! s:Subcommands() abort
|
|
if !has_key(s:exec_paths, g:fugitive_git_executable)
|
|
let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
|
|
endif
|
|
let exec_path = s:exec_paths[g:fugitive_git_executable]
|
|
return map(split(glob(exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(exec_path)+5 : -1],"\\.exe$","")')
|
|
endfunction
|
|
|
|
let s:aliases = {}
|
|
function! s:Aliases(dir) abort
|
|
if !has_key(s:aliases, a:dir)
|
|
let s:aliases[a:dir] = {}
|
|
let lines = split(s:TreeChomp('config','-z','--get-regexp','^alias[.]'),"\1")
|
|
for line in v:shell_error ? [] : lines
|
|
let s:aliases[a:dir][matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*')
|
|
endfor
|
|
endif
|
|
return s:aliases[a:dir]
|
|
endfunction
|
|
|
|
function! fugitive#CompleteGit(lead, ...) abort
|
|
let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
|
|
let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
|
|
if pre !~# ' [[:alnum:]-]\+ '
|
|
let cmds = s:Subcommands()
|
|
return filter(sort(cmds+keys(s:Aliases(dir))), 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
|
|
elseif pre =~# ' -- '
|
|
return fugitive#CompletePath(a:lead, dir)
|
|
else
|
|
return fugitive#CompleteObject(a:lead, dir)
|
|
endif
|
|
endfunction
|
|
|
|
" Section: :Gcd, :Glcd
|
|
|
|
function! s:DirComplete(A, L, P) abort
|
|
return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
|
|
endfunction
|
|
|
|
function! s:DirArg(path) abort
|
|
let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
|
|
if path =~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
|
|
return path
|
|
else
|
|
return (empty(s:Tree()) ? s:Dir() : s:Tree()) . '/' . path
|
|
endif
|
|
endfunction
|
|
|
|
call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd :exe 'cd<bang>' s:fnameescape(s:DirArg(<q-args>))")
|
|
call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :exe 'lcd<bang>' s:fnameescape(s:DirArg(<q-args>))")
|
|
|
|
" Section: :Gstatus
|
|
|
|
call s:command("-bar -bang -range=-1 Gstatus", "Status")
|
|
call s:command("-bar -bang -range=-1 G", "Status")
|
|
|
|
function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
|
|
try
|
|
let mods = a:mods ==# '<mods>' || empty(a:mods) ? '' : a:mods . ' '
|
|
if mods !~# 'aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright'
|
|
let mods = (&splitbelow ? 'botright ' : 'topleft ') . mods
|
|
endif
|
|
let file = fugitive#Find(':')
|
|
let arg = ' +setl\ foldmethod=syntax\ foldlevel=1\|let\ w:fugitive_status=FugitiveGitDir() ' .
|
|
\ s:fnameescape(file)
|
|
for winnr in range(1, winnr('$'))
|
|
if s:cpath(file, fnamemodify(bufname(winbufnr(winnr)), ':p'))
|
|
exe winnr . 'wincmd w'
|
|
let w:fugitive_status = FugitiveGitDir()
|
|
return s:ReloadStatus()
|
|
endif
|
|
endfor
|
|
if a:count ==# 0
|
|
return mods . 'edit' . (a:bang ? '!' : '') . arg
|
|
elseif a:bang
|
|
return mods . 'pedit' . arg . '|wincmd P'
|
|
else
|
|
return mods . (a:count > 0 ? a:count : '') . 'split' . arg
|
|
endif
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
return ''
|
|
endfunction
|
|
|
|
function! s:StageSeek(info, fallback) abort
|
|
let info = a:info
|
|
if empty(info.section)
|
|
return a:fallback
|
|
endif
|
|
let line = search('^' . info.section, 'wn')
|
|
if !line
|
|
for section in get({'Staged': ['Unstaged'], 'Unstaged': ['Staged']}, info.section, [])
|
|
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
|
|
|
|
function! s:ReloadStatus(...) abort
|
|
if get(b:, 'fugitive_type', '') !=# 'index'
|
|
return ''
|
|
endif
|
|
let original_lnum = a:0 ? a:1 : line('.')
|
|
let info = s:StageInfo(original_lnum)
|
|
call fugitive#BufReadStatus()
|
|
exe s:StageSeek(info, original_lnum)
|
|
normal! 0
|
|
return ''
|
|
endfunction
|
|
|
|
function! fugitive#ReloadStatus(...) abort
|
|
if exists('s:reloading_status')
|
|
return
|
|
endif
|
|
try
|
|
let s:reloading_status = 1
|
|
let mytab = tabpagenr()
|
|
for tab in [mytab] + range(1,tabpagenr('$'))
|
|
for winnr in range(1,tabpagewinnr(tab,'$'))
|
|
if getbufvar(tabpagebuflist(tab)[winnr-1],'fugitive_type') ==# 'index'
|
|
execute 'tabnext '.tab
|
|
if winnr != winnr()
|
|
execute winnr.'wincmd w'
|
|
let restorewinnr = 1
|
|
endif
|
|
try
|
|
if !&modified
|
|
exe s:ReloadStatus()
|
|
endif
|
|
finally
|
|
if exists('restorewinnr')
|
|
unlet restorewinnr
|
|
wincmd p
|
|
endif
|
|
execute 'tabnext '.mytab
|
|
endtry
|
|
endif
|
|
endfor
|
|
endfor
|
|
finally
|
|
unlet! s:reloading_status
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:CanAutoReloadStatus() abort
|
|
return get(g:, 'fugitive_autoreload_status', !has('win32'))
|
|
endfunction
|
|
|
|
function! s:AutoReloadStatus(...) abort
|
|
if s:CanAutoReloadStatus()
|
|
return call('fugitive#ReloadStatus', a:000)
|
|
endif
|
|
endfunction
|
|
|
|
augroup fugitive_status
|
|
autocmd!
|
|
autocmd ShellCmdPost * call s:AutoReloadStatus()
|
|
autocmd BufDelete term://* call s:AutoReloadStatus()
|
|
if !has('win32')
|
|
autocmd FocusGained * call s:AutoReloadStatus()
|
|
endif
|
|
augroup END
|
|
|
|
function! s:StageInfo(...) abort
|
|
let lnum = a:0 ? a:1 : line('.')
|
|
let sigil = matchstr(getline(lnum), '^[ @\+-]')
|
|
let offset = -1
|
|
if len(sigil)
|
|
let type = sigil ==# '-' ? '-' : '+'
|
|
while lnum > 0 && getline(lnum) !~# '^@'
|
|
if getline(lnum) =~# '^[ '.type.']'
|
|
let offset += 1
|
|
endif
|
|
let lnum -= 1
|
|
endwhile
|
|
let offset += matchstr(getline(lnum), type.'\zs\d\+')
|
|
while getline(lnum) =~# '^[ @\+-]'
|
|
let lnum -= 1
|
|
endwhile
|
|
endif
|
|
let slnum = lnum + 1
|
|
let section = ''
|
|
let index = 0
|
|
while len(getline(slnum - 1)) && empty(section)
|
|
let slnum -= 1
|
|
let section = matchstr(getline(slnum), '^\u\l\+\ze.* (\d\+)$')
|
|
if empty(section) && getline(slnum) !~# '^[ @\+-]'
|
|
let index += 1
|
|
endif
|
|
endwhile
|
|
return {'section': section,
|
|
\ 'heading': getline(slnum),
|
|
\ 'sigil': sigil,
|
|
\ 'offset': offset,
|
|
\ 'filename': matchstr(getline(lnum), '^[A-Z?] \zs.*'),
|
|
\ '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]'),
|
|
\ 'index': index}
|
|
endfunction
|
|
|
|
function! s:Selection(arg1, ...) abort
|
|
if a:arg1 ==# 'n'
|
|
let arg1 = line('.')
|
|
let arg2 = v:count
|
|
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
|
|
let last = first - arg2 + 1
|
|
elseif arg2 > 0
|
|
let last = arg2
|
|
else
|
|
let last = first
|
|
endif
|
|
while getline(first) =~# '^$\|^[A-Z][a-z]'
|
|
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
|
|
let section = ''
|
|
let index = 0
|
|
while len(getline(slnum - 1)) && empty(section)
|
|
let slnum -= 1
|
|
let heading = matchstr(getline(slnum), '^\u\l\+.* (\d\+)$')
|
|
if empty(heading) && getline(slnum) !~# '^[ @\+-]'
|
|
let index += 1
|
|
endif
|
|
endwhile
|
|
let results = []
|
|
let template = {
|
|
\ 'heading': heading,
|
|
\ 'section': matchstr(heading, '^\u\l\+\ze.* (\d\+)$'),
|
|
\ 'filename': '',
|
|
\ '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
|
|
if line =~# '^\u\l\+\ze.* (\d\+)$'
|
|
let template.heading = getline(lnum)
|
|
let template.section = matchstr(template.heading, '^\u\l\+\ze.* (\d\+)$')
|
|
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?] '
|
|
let filename = matchstr(line, '^[A-Z?] \zs.*')
|
|
call add(results, extend(deepcopy(template), {
|
|
\ 'lnum': lnum,
|
|
\ 'filename': filename,
|
|
\ 'paths': map(reverse(split(filename, ' -> ')), 'root . v:val'),
|
|
\ '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('.')
|
|
if !a:0 && !v:count && line =~# '^[A-Z][a-z]'
|
|
let header = matchstr(line, '^\S\+\ze:')
|
|
if len(header) && exists('*s:Do' . a:action . header . 'Header')
|
|
call s:Do{a:action}{header}Header(matchstr(line, ': \zs.*'))
|
|
endif
|
|
let section = matchstr(line, '^\S\+')
|
|
if exists('*s:Do' . a:action . section . 'Heading')
|
|
call s:Do{a:action}{section}Heading(line)
|
|
return s:ReloadStatus()
|
|
endif
|
|
endif
|
|
let selection = s:Selection(a:visual ? 'v' : 'n')
|
|
if empty(selection)
|
|
return ''
|
|
endif
|
|
call filter(selection, 'v:val.section ==# selection[0].section')
|
|
let reload = 0
|
|
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
|
|
call s:StageReveal()
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
finally
|
|
if reload
|
|
execute s:ReloadStatus()
|
|
endif
|
|
endtry
|
|
return ''
|
|
endfunction
|
|
|
|
function! s:StageReveal(...) abort
|
|
let begin = a:0 ? a:1 : line('.')
|
|
if getline(begin) =~# '^@'
|
|
let end = begin + 1
|
|
while getline(end) =~# '^[ \+-]'
|
|
let end += 1
|
|
endwhile
|
|
while end > line('w$') && line('.') > line('w0') + &scrolloff
|
|
execute "normal! \<C-E>"
|
|
endwhile
|
|
endif
|
|
endfunction
|
|
|
|
function! s:StageNext(count) abort
|
|
for i in range(a:count)
|
|
call search('^[A-Z?] .\|^[0-9a-f]\{4,\} \|^@','W')
|
|
endfor
|
|
call s:StageReveal()
|
|
return '.'
|
|
endfunction
|
|
|
|
function! s:StagePrevious(count) abort
|
|
if line('.') == 1 && exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^<c-p>$'
|
|
return 'CtrlP '.fnameescape(s:Tree())
|
|
else
|
|
for i in range(a:count)
|
|
call search('^[A-Z?] .\|^[0-9a-f]\{4,\} \|^@','Wbe')
|
|
endfor
|
|
call s:StageReveal()
|
|
return '.'
|
|
endif
|
|
endfunction
|
|
|
|
function! s:StageInline(mode, ...) abort
|
|
let lnum1 = a:0 ? a:1 : line('.')
|
|
let lnum = lnum1 + 1
|
|
if a:0 > 1 && a:2 == 0
|
|
let info = s:StageInfo(lnum - 1)
|
|
if empty(info.filename) && len(info.section)
|
|
while len(getline(lnum))
|
|
let lnum += 1
|
|
endwhile
|
|
endif
|
|
elseif a:0 > 1
|
|
let lnum += a:2 - 1
|
|
endif
|
|
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
|
|
if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADM]$' || a:mode ==# 'hide'
|
|
continue
|
|
endif
|
|
let mode = ''
|
|
let diff = []
|
|
let index = 0
|
|
let start = -1
|
|
for line in b:fugitive_diff[info.section]
|
|
if mode ==# 'await' && line[0] ==# '@'
|
|
let mode = 'capture'
|
|
endif
|
|
if mode !=# 'head' && line !~# '^[ @\+-]'
|
|
if len(diff)
|
|
break
|
|
endif
|
|
let start = index
|
|
let mode = 'head'
|
|
elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '--- ' . info.filename
|
|
let mode = 'await'
|
|
elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '+++ ' . info.filename
|
|
let mode = 'await'
|
|
elseif mode ==# 'capture'
|
|
call add(diff, line)
|
|
elseif line[0] ==# '@'
|
|
let mode = ''
|
|
endif
|
|
let index += 1
|
|
endfor
|
|
if len(diff)
|
|
setlocal modifiable noreadonly
|
|
silent call append(lnum, diff)
|
|
let b:fugitive_expanded[info.section][info.filename] = [start, len(diff)]
|
|
setlocal nomodifiable readonly nomodified
|
|
endif
|
|
endwhile
|
|
return lnum
|
|
endfunction
|
|
|
|
function! s:StageIntend(count) abort
|
|
for i in range(a:count)
|
|
if getline('.')[0:1] ==# '? '
|
|
call s:TreeChomp('add', '--intent-to-add', '--', s:Tree() . '/' . getline('.')[2:-1])
|
|
-
|
|
exe s:ReloadStatus()
|
|
elseif getline('.') =~# '^Unstaged'
|
|
call s:TreeChomp('add', '--intent-to-add', '--', s:Tree())
|
|
exe s:ReloadStatus()
|
|
else
|
|
call s:StageInline('show', line('.'), 1)
|
|
endif
|
|
call s:StageNext(1)
|
|
endfor
|
|
return '.'
|
|
endfunction
|
|
|
|
function! s:StageDiff(diff) abort
|
|
let lnum = line('.')
|
|
let info = s:StageInfo(lnum)
|
|
let prefix = info.offset > 0 ? '+' . info.offset : ''
|
|
if empty(info.filename) && info.section ==# 'Staged'
|
|
return 'Git! diff --no-ext-diff --cached'
|
|
elseif empty(info.filename)
|
|
return 'Git! diff --no-ext-diff'
|
|
elseif info.filename =~# ' -> '
|
|
let [old, new] = split(info.filename,' -> ')
|
|
execute 'Gedit' . prefix s:fnameescape(':0:'.new)
|
|
return a:diff.' HEAD:'.s:fnameescape(old)
|
|
elseif info.section ==# 'Staged' && info.sigil ==# '-'
|
|
execute 'Gedit' prefix s:fnameescape('@:'.info.filename)
|
|
return a:diff.'! :0'
|
|
elseif info.section ==# 'Staged'
|
|
execute 'Gedit' prefix s:fnameescape(':0:'.info.filename)
|
|
return a:diff . (info.sigil ==# '+' ? '!' : '') . ' -'
|
|
elseif info.sigil ==# '-'
|
|
execute 'Gedit' prefix s:fnameescape(':0:'.info.filename)
|
|
return a:diff . '!'
|
|
else
|
|
execute 'Gedit' prefix s:fnameescape(':(top)'.info.filename)
|
|
return a:diff . (info.sigil ==# '+' ? '!' : '')
|
|
endif
|
|
endfunction
|
|
|
|
function! s:StageDiffEdit() abort
|
|
let info = s:StageInfo(line('.'))
|
|
let arg = (empty(info.filename) ? '.' : info.filename)
|
|
if info.section ==# 'Staged'
|
|
return 'Git! diff --no-ext-diff --cached '.s:shellesc(arg)
|
|
elseif info.status ==# '?'
|
|
call s:TreeChomp('add', '--intent-to-add', './' . arg)
|
|
return s:ReloadStatus()
|
|
else
|
|
return 'Git! diff --no-ext-diff '.s:shellesc(arg)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:StageApply(info, reverse, extra) abort
|
|
let cmd = ['apply', '-p0', '--recount'] + a:extra
|
|
let info = a:info
|
|
let start = info.patch
|
|
let end = info.lnum
|
|
let lines = getline(start, end)
|
|
if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
|
|
return -1
|
|
endif
|
|
while getline(end) =~# '^[-+ ]'
|
|
let end += 1
|
|
if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
|
|
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
|
|
if start == 0 || getline(start) !~# '^@@ '
|
|
call s:throw("could not find hunk")
|
|
endif
|
|
let i = b:fugitive_expanded[info.section][info.filename][0]
|
|
let head = []
|
|
while get(b:fugitive_diff[info.section], i, '@') !~# '^@'
|
|
call add(head, b:fugitive_diff[info.section][i])
|
|
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])
|
|
let output = call('s:TreeChomp', cmd)
|
|
if !v:shell_error
|
|
return 1
|
|
endif
|
|
call s:throw(output)
|
|
endfunction
|
|
|
|
function! s:StageDelete(lnum, count) abort
|
|
let info = get(s:Selection(a:lnum, -a:count), 0, {'filename': ''})
|
|
if empty(info.filename)
|
|
return ''
|
|
endif
|
|
let hash = s:TreeChomp('hash-object', '-w', '--', info.paths[0])
|
|
if empty(hash)
|
|
return ''
|
|
elseif info.patch
|
|
try
|
|
call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
elseif a:count == 2
|
|
call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
|
|
elseif a:count == 3
|
|
call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
|
|
elseif info.status =~# '[ADU]' &&
|
|
\ get(b:fugitive_status[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, '') =~# '[AU]'
|
|
call s:TreeChomp('checkout', info.section ==# 'Staged' ? '--ours' : '--theirs', '--', info.paths[0])
|
|
elseif info.status ==# 'U'
|
|
call s:TreeChomp('rm', '--', info.paths[0])
|
|
elseif info.status ==# 'A'
|
|
call s:TreeChomp('rm', '-f', '--', info.paths[0])
|
|
elseif info.status ==# '?'
|
|
call s:TreeChomp('clean', '-f', '--', info.paths[0])
|
|
elseif info.section ==# 'Unstaged'
|
|
call s:TreeChomp('checkout', '--', info.paths[0])
|
|
else
|
|
call s:TreeChomp('checkout', 'HEAD^{}', '--', info.paths[0])
|
|
endif
|
|
exe s:ReloadStatus()
|
|
let @@ = hash
|
|
return 'checktime|redraw|echomsg ' .
|
|
\ string('To restore, :Git cat-file blob '.hash[0:6].' > '.info.filename)
|
|
endfunction
|
|
|
|
function! s:DoToggleHeadHeader(value) abort
|
|
exe 'edit' s:fnameescape(s:Dir())
|
|
call search('\C^index$', 'wc')
|
|
endfunction
|
|
|
|
function! s:DoToggleUnpushedHeading(heading) abort
|
|
let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
|
|
if empty(remote)
|
|
let remote = '.'
|
|
endif
|
|
let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
|
|
call feedkeys(':Gpush ' . remote . ' ' . 'HEAD:' . branch)
|
|
endfunction
|
|
|
|
function! s:DoToggleUnpushed(record) abort
|
|
let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
|
|
if empty(remote)
|
|
let remote = '.'
|
|
endif
|
|
let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
|
|
call feedkeys(':Gpush ' . remote . ' ' . a:record.commit . ':' . branch)
|
|
endfunction
|
|
|
|
function! s:DoToggleUnpulledHeading(heading) abort
|
|
call feedkeys(':Grebase')
|
|
endfunction
|
|
|
|
function! s:DoToggleUnpulled(record) abort
|
|
call feedkeys(':Grebase ' . a:record.commit)
|
|
endfunction
|
|
|
|
function! s:DoToggleStagedHeading(...) abort
|
|
call s:TreeChomp('reset', '-q')
|
|
return 1
|
|
endfunction
|
|
|
|
function! s:DoUnstageStagedHeading(heading) abort
|
|
return s:DoToggleStagedHeading(a:heading)
|
|
endfunction
|
|
|
|
function! s:DoToggleUnstagedHeading(...) abort
|
|
call s:TreeChomp('add', '-u')
|
|
return 1
|
|
endfunction
|
|
|
|
function! s:DoStageUnstagedHeading(heading) abort
|
|
return s:DoToggleUnstagedHeading(a:heading)
|
|
endfunction
|
|
|
|
function! s:DoToggleStaged(record) abort
|
|
if a:record.patch
|
|
return s:StageApply(a:record, 1, ['--cached'])
|
|
else
|
|
call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
|
|
return 1
|
|
endif
|
|
endfunction
|
|
|
|
function! s:DoStageStaged(record) abort
|
|
return -1
|
|
endfunction
|
|
|
|
function! s:DoUnstageStaged(record) abort
|
|
return s:DoToggleStaged(a:record)
|
|
endfunction
|
|
|
|
function! s:DoToggleUnstaged(record) abort
|
|
if a:record.patch
|
|
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
|
|
endif
|
|
endfunction
|
|
|
|
function! s:StagePatch(lnum1,lnum2) abort
|
|
let add = []
|
|
let reset = []
|
|
|
|
for lnum in range(a:lnum1,a:lnum2)
|
|
let info = s:StageInfo(lnum)
|
|
if empty(info.filename) && info.section ==# 'Staged'
|
|
return 'Git reset --patch'
|
|
elseif empty(info.filename) && info.section ==# 'Unstaged'
|
|
return 'Git add --patch'
|
|
elseif info.filename ==# ''
|
|
continue
|
|
endif
|
|
execute lnum
|
|
if info.filename =~ ' -> '
|
|
let reset += [split(info.filename,' -> ')[1]]
|
|
elseif info.section ==# 'Staged'
|
|
let reset += [info.filename]
|
|
elseif info.status !~# '^D'
|
|
let add += [info.filename]
|
|
endif
|
|
endfor
|
|
try
|
|
if !empty(add)
|
|
execute "Git add --patch -- ".join(map(add,'s:shellesc(v:val)'))
|
|
endif
|
|
if !empty(reset)
|
|
execute "Git reset --patch -- ".join(map(reset,'s:shellesc(v:val)'))
|
|
endif
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
return s:ReloadStatus()
|
|
endfunction
|
|
|
|
" Section: :Gcommit
|
|
|
|
call s:command("-nargs=? -complete=customlist,s:CommitComplete Gcommit", "Commit")
|
|
|
|
function! s:CommitCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
|
|
let mods = s:gsub(a:mods ==# '<mods>' ? '' : a:mods, '<tab>', '-tab')
|
|
let dir = a:0 ? a:1 : s:Dir()
|
|
let tree = s:Tree(dir)
|
|
let msgfile = dir . '/COMMIT_EDITMSG'
|
|
let outfile = tempname()
|
|
let errorfile = tempname()
|
|
try
|
|
let guioptions = &guioptions
|
|
try
|
|
if &guioptions =~# '!'
|
|
setglobal guioptions-=!
|
|
endif
|
|
let cdback = s:Cd(tree)
|
|
if s:winshell()
|
|
let command = ''
|
|
let old_editor = $GIT_EDITOR
|
|
let $GIT_EDITOR = 'false'
|
|
else
|
|
let command = 'env GIT_EDITOR=false '
|
|
endif
|
|
let args = s:ShellExpand(a:arg)
|
|
let command .= s:UserCommand() . ' commit ' . args
|
|
if &shell =~# 'csh'
|
|
noautocmd silent execute '!('.escape(command, '!#%').' > '.outfile.') >& '.errorfile
|
|
elseif a:arg =~# '\%(^\| \)-\%(-interactive\|p\|-patch\)\>'
|
|
noautocmd execute '!'.command.' 2> '.errorfile
|
|
else
|
|
noautocmd silent execute '!'.command.' > '.outfile.' 2> '.errorfile
|
|
endif
|
|
let error = v:shell_error
|
|
finally
|
|
execute cdback
|
|
let &guioptions = guioptions
|
|
endtry
|
|
if !has('gui_running')
|
|
redraw!
|
|
endif
|
|
if !error
|
|
if filereadable(outfile)
|
|
for line in readfile(outfile)
|
|
echo line
|
|
endfor
|
|
endif
|
|
call fugitive#ReloadStatus()
|
|
return ''
|
|
else
|
|
let errors = readfile(errorfile)
|
|
let error = get(errors,-2,get(errors,-1,'!'))
|
|
if error =~# 'false''\=\.$'
|
|
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-[esp]|--edit|--interactive|--patch|--signoff)%($| )','')
|
|
let args = s:gsub(args,'%(%(^| )-- )@<!%(^| )@<=%(-c|--reedit-message|--reuse-message|-F|--file|-m|--message)%(\s+|\=)%(''[^'']*''|"%(\\.|[^"])*"|\\.|\S)*','')
|
|
let args = s:sub(args, '\ze -- |$', ' --no-edit --no-interactive --no-signoff')
|
|
let args = '-F '.s:shellesc(msgfile).' '.args
|
|
if args !~# '\%(^\| \)--cleanup\>'
|
|
let args = '--cleanup=strip '.args
|
|
endif
|
|
if bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&mod
|
|
execute mods 'keepalt edit' s:fnameescape(msgfile)
|
|
elseif a:arg =~# '\%(^\| \)-\w*v' || mods =~# '\<tab\>'
|
|
execute mods 'keepalt -tabedit' s:fnameescape(msgfile)
|
|
else
|
|
execute mods 'keepalt split' s:fnameescape(msgfile)
|
|
endif
|
|
let b:fugitive_commit_arguments = args
|
|
setlocal bufhidden=wipe filetype=gitcommit
|
|
return '1'
|
|
elseif error ==# '!'
|
|
echo get(readfile(outfile), -1, '')
|
|
return ''
|
|
else
|
|
call s:throw(empty(error)?join(errors, ' '):error)
|
|
endif
|
|
endif
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
finally
|
|
if exists('old_editor')
|
|
let $GIT_EDITOR = old_editor
|
|
endif
|
|
call delete(outfile)
|
|
call delete(errorfile)
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:CommitComplete(A,L,P) abort
|
|
if a:A =~# '^--fixup=\|^--squash='
|
|
let commits = split(s:TreeChomp('log', '--pretty=format:%s', '@{upstream}..'), "\n")
|
|
if !v:shell_error
|
|
let pre = matchstr(a:A, '^--\w*=') . ':/^'
|
|
return map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")')
|
|
endif
|
|
elseif a:A =~ '^-' || type(a:A) == type(0) " a:A is 0 on :Gcommit -<Tab>
|
|
let args = ['-C', '-F', '-a', '-c', '-e', '-i', '-m', '-n', '-o', '-q', '-s', '-t', '-u', '-v', '--all', '--allow-empty', '--amend', '--author=', '--cleanup=', '--dry-run', '--edit', '--file=', '--fixup=', '--include', '--interactive', '--message=', '--no-verify', '--only', '--quiet', '--reedit-message=', '--reuse-message=', '--signoff', '--squash=', '--template=', '--untracked-files', '--verbose']
|
|
return filter(args,'v:val[0 : strlen(a:A)-1] ==# a:A')
|
|
else
|
|
return fugitive#CompletePath(a:A, s:Dir())
|
|
endif
|
|
return []
|
|
endfunction
|
|
|
|
function! s:FinishCommit() abort
|
|
let buf = +expand('<abuf>')
|
|
let args = getbufvar(buf, 'fugitive_commit_arguments')
|
|
if !empty(args)
|
|
call setbufvar(buf, 'fugitive_commit_arguments', '')
|
|
if getbufvar(buf, 'fugitive_commit_rebase')
|
|
call setbufvar(buf, 'fugitive_commit_rebase', 0)
|
|
let s:rebase_continue = s:Dir(buf)
|
|
endif
|
|
return s:CommitCommand(-1, -1, 0, -1, 0, '', '', args, [], s:Dir(buf))
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
" Section: :Gmerge, :Grebase, :Gpull
|
|
|
|
call s:command("-nargs=? -bang -complete=custom,s:RevisionComplete Gmerge " .
|
|
\ "execute s:Merge('merge', <bang>0, '<mods>', <q-args>)")
|
|
call s:command("-nargs=? -bang -complete=custom,s:RevisionComplete Grebase " .
|
|
\ "execute s:Merge('rebase', <bang>0, '<mods>', <q-args>)")
|
|
call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gpull " .
|
|
\ "execute s:Merge('pull --progress', <bang>0, '<mods>', <q-args>)")
|
|
|
|
function! s:RevisionComplete(A, L, P) abort
|
|
return s:TreeChomp('rev-parse', '--symbolic', '--branches', '--tags', '--remotes')
|
|
\ . "\nHEAD\nFETCH_HEAD\nMERGE_HEAD\nORIG_HEAD"
|
|
endfunction
|
|
|
|
function! s:RemoteComplete(A, L, P) abort
|
|
let remote = matchstr(a:L, ' \zs\S\+\ze ')
|
|
if !empty(remote)
|
|
let matches = split(s:TreeChomp('ls-remote', remote), "\n")
|
|
call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
|
|
call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
|
|
else
|
|
let matches = split(s:TreeChomp('remote'), "\n")
|
|
endif
|
|
return join(matches, "\n")
|
|
endfunction
|
|
|
|
function! s:RebaseSequenceAborter() abort
|
|
if !exists('s:rebase_sequence_aborter')
|
|
let temp = tempname() . '.sh'
|
|
call writefile(
|
|
\ ['#!/bin/sh',
|
|
\ 'echo exec false | cat - "$1" > "$1.fugitive"',
|
|
\ 'mv "$1.fugitive" "$1"'],
|
|
\ temp)
|
|
let s:rebase_sequence_aborter = temp
|
|
endif
|
|
return s:rebase_sequence_aborter
|
|
endfunction
|
|
|
|
function! fugitive#Cwindow() abort
|
|
if &buftype == 'quickfix'
|
|
cwindow
|
|
else
|
|
botright cwindow
|
|
if &buftype == 'quickfix'
|
|
wincmd p
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
let s:common_efm = ''
|
|
\ . '%+Egit:%.%#,'
|
|
\ . '%+Eusage:%.%#,'
|
|
\ . '%+Eerror:%.%#,'
|
|
\ . '%+Efatal:%.%#,'
|
|
\ . '%-G%.%#%\e[K%.%#,'
|
|
\ . '%-G%.%#%\r%.%\+'
|
|
|
|
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',
|
|
\ }
|
|
|
|
function! s:RebaseEdit(cmd, dir) abort
|
|
return a:cmd . ' +setlocal\ bufhidden=wipe ' . s:fnameescape(a:dir . '/rebase-merge/git-rebase-todo')
|
|
endfunction
|
|
|
|
function! s:Merge(cmd, bang, mods, args, ...) abort
|
|
let dir = a:0 ? a:1 : s:Dir()
|
|
let mods = substitute(a:mods, '\C<mods>', '', '') . ' '
|
|
if a:cmd =~# '^rebase' && ' '.a:args =~# ' -i\| --interactive'
|
|
let cmd = fugitive#Prepare(dir, '-c', 'sequence.editor=sh ' . s:RebaseSequenceAborter(), 'rebase') . ' ' . a:args
|
|
let out = system(cmd)[0:-2]
|
|
for file in ['end', 'msgnum']
|
|
let file = fugitive#Find('.git/rebase-merge/' . file, dir)
|
|
if !filereadable(file)
|
|
return 'echoerr ' . string("fugitive: " . out)
|
|
endif
|
|
call writefile([readfile(file)[0] - 1], file)
|
|
endfor
|
|
call writefile([], fugitive#Find('.git/rebase-merge/done', dir))
|
|
if a:bang
|
|
return ''
|
|
endif
|
|
return s:RebaseEdit(mods . 'split', dir)
|
|
elseif a:cmd =~# '^rebase' && ' '.a:args =~# ' --edit-todo' && filereadable(dir . '/rebase-merge/git-rebase-todo')
|
|
return s:RebaseEdit(mods . 'split', dir)
|
|
elseif a:cmd =~# '^rebase' && ' '.a:args =~# ' --continue' && !a:0
|
|
let rdir = dir . '/rebase-merge'
|
|
call system(fugitive#Prepare(dir, 'diff-index', '--cached', '--quiet', 'HEAD', '--'))
|
|
if v:shell_error && isdirectory(rdir)
|
|
if getfsize(rdir . '/amend') <= 0
|
|
return 'exe ' . string(mods . 'Gcommit -n -F ' . s:shellesc(dir . '/rebase-merge/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
|
|
elseif readfile(rdir . '/amend')[0] ==# fugitive#Head(-1, dir)
|
|
return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:shellesc(dir . '/rebase-merge/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
|
|
endif
|
|
endif
|
|
endif
|
|
let [mp, efm] = [&l:mp, &l:efm]
|
|
let had_merge_msg = filereadable(dir . '/MERGE_MSG')
|
|
try
|
|
let cdback = s:Cd(s:Tree(dir))
|
|
let &l:errorformat = ''
|
|
\ . '%-Gerror:%.%#false''.,'
|
|
\ . '%-G%.%# ''git commit'' %.%#,'
|
|
\ . '%+Emerge:%.%#,'
|
|
\ . s:common_efm . ','
|
|
\ . '%+ECannot %.%#: You have unstaged changes.,'
|
|
\ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
|
|
\ . '%+EThere is no tracking information for the current branch.,'
|
|
\ . '%+EYou are not currently on a branch. Please specify which,'
|
|
\ . '%+I %#git rebase --continue,'
|
|
\ . 'CONFLICT (%m): %f deleted in %.%#,'
|
|
\ . 'CONFLICT (%m): Merge conflict in %f,'
|
|
\ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
|
|
\ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
|
|
\ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
|
|
\ . '%+ECONFLICT %.%#,'
|
|
\ . '%+EKONFLIKT %.%#,'
|
|
\ . '%+ECONFLIT %.%#,'
|
|
\ . "%+EXUNG \u0110\u1ed8T %.%#,"
|
|
\ . "%+E\u51b2\u7a81 %.%#,"
|
|
\ . 'U%\t%f'
|
|
if a:cmd =~# '^merge' && empty(a:args) &&
|
|
\ (had_merge_msg || isdirectory(dir . '/rebase-apply') ||
|
|
\ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
|
|
let &l:makeprg = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
|
|
else
|
|
let &l:makeprg = s:sub(s:UserCommand() . ' ' . a:cmd .
|
|
\ (' ' . a:args =~# ' \%(--no-edit\|--abort\|-m\)\>' || a:cmd =~# '^rebase' ? '' : ' --edit') .
|
|
\ (' ' . a:args =~# ' --autosquash\>' && a:cmd =~# '^rebase' ? ' --interactive' : '') .
|
|
\ ' ' . a:args, ' *$', '')
|
|
endif
|
|
if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
|
|
let old_sequence_editor = $GIT_SEQUENCE_EDITOR
|
|
let $GIT_SEQUENCE_EDITOR = 'true'
|
|
else
|
|
let &l:makeprg = 'env GIT_SEQUENCE_EDITOR=true ' . &l:makeprg
|
|
endif
|
|
if !empty($GIT_EDITOR) || has('win32')
|
|
let old_editor = $GIT_EDITOR
|
|
let $GIT_EDITOR = 'false'
|
|
else
|
|
let &l:makeprg = 'env GIT_EDITOR=false ' . substitute(&l:makeprg, '^env ', '', '')
|
|
endif
|
|
silent noautocmd make!
|
|
catch /^Vim\%((\a\+)\)\=:E211/
|
|
let err = v:exception
|
|
finally
|
|
redraw!
|
|
let [&l:mp, &l:efm] = [mp, efm]
|
|
if exists('old_editor')
|
|
let $GIT_EDITOR = old_editor
|
|
endif
|
|
if exists('old_sequence_editor')
|
|
let $GIT_SEQUENCE_EDITOR = old_sequence_editor
|
|
endif
|
|
execute cdback
|
|
endtry
|
|
call fugitive#ReloadStatus()
|
|
if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
|
|
if a:cmd =~# '^rebase' &&
|
|
\ filereadable(dir . '/rebase-merge/amend') &&
|
|
\ filereadable(dir . '/rebase-merge/done') &&
|
|
\ get(readfile(dir . '/rebase-merge/done'), -1, '') =~# '^[^e]'
|
|
cclose
|
|
return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:shellesc(dir . '/rebase-merge/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
|
|
elseif !had_merge_msg && filereadable(dir . '/MERGE_MSG')
|
|
cclose
|
|
return mods . 'Gcommit --no-status -n -t '.s:shellesc(dir . '/MERGE_MSG')
|
|
endif
|
|
endif
|
|
let qflist = getqflist()
|
|
let found = 0
|
|
for e in qflist
|
|
if !empty(e.bufnr)
|
|
let found = 1
|
|
let e.pattern = '^<<<<<<<'
|
|
endif
|
|
endfor
|
|
call fugitive#Cwindow()
|
|
if found
|
|
call setqflist(qflist, 'r')
|
|
if !a:bang
|
|
return 'cfirst'
|
|
endif
|
|
endif
|
|
return exists('err') ? 'echoerr '.string(err) : ''
|
|
endfunction
|
|
|
|
function! s:RebaseClean(file) abort
|
|
if !filereadable(a:file)
|
|
return ''
|
|
endif
|
|
let old = readfile(a:file)
|
|
let new = copy(old)
|
|
for i in range(len(new))
|
|
let new[i] = substitute(new[i], '^\l\>', '\=get(s:rebase_abbrevs,submatch(0),submatch(0))', '')
|
|
endfor
|
|
if new !=# old
|
|
call writefile(new, a:file)
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
augroup fugitive_merge
|
|
autocmd!
|
|
autocmd VimLeavePre,BufDelete git-rebase-todo
|
|
\ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
|
|
\ call s:RebaseClean(expand('<afile>')) |
|
|
\ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
|
|
\ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) |
|
|
\ endif |
|
|
\ endif
|
|
autocmd BufEnter * nested
|
|
\ if exists('s:rebase_continue') |
|
|
\ exe s:Merge('rebase', 0, '', getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort', remove(s:, 'rebase_continue')) |
|
|
\ endif
|
|
augroup END
|
|
|
|
" Section: :Ggrep, :Glog
|
|
|
|
if !exists('g:fugitive_summary_format')
|
|
let g:fugitive_summary_format = '%s'
|
|
endif
|
|
|
|
function! s:GrepComplete(A, L, P) abort
|
|
if strpart(a:L, 0, a:P) =~# ' -- '
|
|
return fugitive#CompletePath(a:A, s:Dir())
|
|
else
|
|
return fugitive#CompleteObject(a:A, s:Dir())
|
|
endif
|
|
endfunction
|
|
|
|
call s:command("-bang -nargs=? -complete=customlist,s:GrepComplete Ggrep :execute s:Grep('grep',<bang>0,<q-args>)")
|
|
call s:command("-bang -nargs=? -complete=customlist,s:GrepComplete Glgrep :execute s:Grep('lgrep',<bang>0,<q-args>)")
|
|
call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:GrepComplete Glog :call s:Log('grep',<bang>0,<line1>,<count>,<q-args>)")
|
|
call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:GrepComplete Gllog :call s:Log('lgrep',<bang>0,<line1>,<count>,<q-args>)")
|
|
|
|
function! s:Grep(cmd,bang,arg) abort
|
|
let grepprg = &grepprg
|
|
let grepformat = &grepformat
|
|
try
|
|
let cdback = s:Cd(s:Tree())
|
|
let &grepprg = s:UserCommand() . ' --no-pager grep -n --no-color'
|
|
let &grepformat = '%f:%l:%c:%m,%f:%l:%m,%m %f match%ts,%f'
|
|
if fugitive#GitVersion(2, 19)
|
|
let &grepprg .= ' --column'
|
|
endif
|
|
exe a:cmd.'! '.escape(s:ShellExpand(matchstr(a:arg, '\v\C.{-}%($|[''" ]\@=\|)@=')), '|#%')
|
|
let list = a:cmd =~# '^l' ? getloclist(0) : getqflist()
|
|
for entry in list
|
|
if bufname(entry.bufnr) =~ ':'
|
|
let entry.filename = s:Generate(bufname(entry.bufnr))
|
|
unlet! entry.bufnr
|
|
let changed = 1
|
|
elseif a:arg =~# '\%(^\| \)--cached\>'
|
|
let entry.filename = s:Generate(':0:'.bufname(entry.bufnr))
|
|
unlet! entry.bufnr
|
|
let changed = 1
|
|
endif
|
|
endfor
|
|
if a:cmd =~# '^l' && exists('changed')
|
|
call setloclist(0, list, 'r')
|
|
elseif exists('changed')
|
|
call setqflist(list, 'r')
|
|
endif
|
|
if !a:bang && !empty(list)
|
|
return (a:cmd =~# '^l' ? 'l' : 'c').'first'.matchstr(a:arg,'\v\C[''" ]\zs\|.*')
|
|
else
|
|
return matchstr(a:arg,'\v\C[''" ]\|\zs.*')
|
|
endif
|
|
finally
|
|
let &grepprg = grepprg
|
|
let &grepformat = grepformat
|
|
execute cdback
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:Log(cmd, bang, line1, line2, ...) abort
|
|
let args = ' ' . join(a:000, ' ')
|
|
let before = substitute(args, ' --\S\@!.*', '', '')
|
|
let after = strpart(args, len(before))
|
|
let path = s:Relative('/')
|
|
let relative = path[1:-1]
|
|
if path =~# '^/\.git\%(/\|$\)' || len(after)
|
|
let path = ''
|
|
endif
|
|
if before !~# '\s[^[:space:]-]'
|
|
let owner = s:Owner(@%)
|
|
if len(owner)
|
|
let before .= ' ' . s:shellesc(owner)
|
|
endif
|
|
endif
|
|
if relative =~# '^\.git\%(/\|$\)'
|
|
let relative = ''
|
|
endif
|
|
if len(relative) && a:line2 > 0
|
|
let before .= ' -L ' . s:shellesc(a:line1 . ',' . a:line2 . ':' . relative)
|
|
elseif len(relative) && (empty(after) || a:line2 == 0)
|
|
let after = (len(after) > 3 ? after : ' -- ') . relative
|
|
endif
|
|
let grepformat = &grepformat
|
|
let grepprg = &grepprg
|
|
try
|
|
let cdback = s:Cd(s:Tree())
|
|
let format = before =~# ' -g\| --walk-reflogs' ? '%gD %gs' : g:fugitive_summary_format
|
|
let &grepprg = escape(s:UserCommand() . ' --no-pager log --no-color ' .
|
|
\ s:shellesc('--pretty=format:fugitive://'.s:Dir().'//%H'.path.'::'.format), '%#')
|
|
let &grepformat = '%Cdiff %.%#,%C--- %.%#,%C+++ %.%#,%Z@@ -%\d%\+\,%\d%\+ +%l\,%\d%\+ @@,%-G-%.%#,%-G+%.%#,%-G %.%#,%A%f::%m,%-G%.%#'
|
|
exe a:cmd . (a:bang ? '! ' : ' ') . s:ShellExpand(before . after)
|
|
if len(path) && a:line2 == -1
|
|
redraw
|
|
echohl WarningMsg
|
|
echo ':Glog will soon default to all files. Use :0Glog to target current file'
|
|
echohl NONE
|
|
endif
|
|
finally
|
|
let &grepformat = grepformat
|
|
let &grepprg = grepprg
|
|
execute cdback
|
|
endtry
|
|
endfunction
|
|
|
|
" Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
|
|
|
|
function! s:UsableWin(nr) abort
|
|
return a:nr && !getwinvar(a:nr, '&previewwindow') &&
|
|
\ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
|
|
\ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
|
|
\ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
|
|
endfunction
|
|
|
|
function! s:OpenParse(args) abort
|
|
let pre = []
|
|
let args = copy(a:args)
|
|
while !empty(args) && args[0] =~# '^+'
|
|
call add(pre, ' ' . escape(remove(args, 0), ' |"'))
|
|
endwhile
|
|
if len(args)
|
|
let file = join(args)
|
|
elseif empty(expand('%'))
|
|
let file = ':'
|
|
elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>'
|
|
let file = ':0:%'
|
|
else
|
|
let file = '%'
|
|
endif
|
|
return [s:Expand(file), join(pre)]
|
|
endfunction
|
|
|
|
function! s:BlurStatus() abort
|
|
if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
|
|
let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
|
|
if len(winnrs)
|
|
exe winnrs[0].'wincmd w'
|
|
else
|
|
belowright new
|
|
endif
|
|
if &diff
|
|
let mywinnr = winnr()
|
|
for winnr in range(winnr('$'),1,-1)
|
|
if winnr != mywinnr && getwinvar(winnr,'&diff')
|
|
execute winnr.'wincmd w'
|
|
close
|
|
if winnr('$') > 1
|
|
wincmd p
|
|
endif
|
|
endif
|
|
endfor
|
|
diffoff!
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
function! s:Open(cmd, bang, mods, arg, args) abort
|
|
let mods = a:mods ==# '<mods>' ? '' : a:mods
|
|
|
|
if a:bang
|
|
let temp = tempname()
|
|
try
|
|
let cdback = s:Cd(s:Tree())
|
|
let git = s:UserCommand()
|
|
let args = s:ShellExpand(a:arg)
|
|
silent! execute '!' . escape(git . ' --no-pager ' . args, '!#%') .
|
|
\ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
|
|
finally
|
|
execute cdback
|
|
endtry
|
|
let temp = s:Resolve(temp)
|
|
let s:temp_files[s:cpath(temp)] = { 'dir': s:Dir(), 'filetype': 'git' }
|
|
if a:cmd ==# 'edit'
|
|
call s:BlurStatus()
|
|
endif
|
|
silent execute mods a:cmd temp
|
|
call fugitive#ReloadStatus()
|
|
return 'redraw|echo ' . string(':!' . git . ' ' . args)
|
|
endif
|
|
|
|
let [file, pre] = s:OpenParse(a:args)
|
|
try
|
|
let file = s:Generate(file)
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
if file !~# '^\a\a\+:'
|
|
let file = s:sub(file, '/$', '')
|
|
endif
|
|
if a:cmd ==# 'edit'
|
|
call s:BlurStatus()
|
|
endif
|
|
return mods . ' ' . a:cmd . pre . ' ' . s:fnameescape(file)
|
|
endfunction
|
|
|
|
function! s:ReadCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
|
|
let mods = a:mods ==# '<mods>' ? '' : a:mods
|
|
let after = a:line2
|
|
if a:count < 0
|
|
let delete = 'silent 1,' . line('$') . 'delete_|'
|
|
let after = line('$')
|
|
elseif a:range == 2
|
|
let delete = 'silent ' . a:line1 . ',' . a:line2 . 'delete_|'
|
|
else
|
|
let delete = ''
|
|
endif
|
|
if a:bang
|
|
try
|
|
let cdback = s:Cd(s:Tree())
|
|
let git = s:UserCommand()
|
|
let args = s:ShellExpand(a:arg)
|
|
silent execute mods after.'read!' escape(git . ' --no-pager ' . args, '!#%')
|
|
finally
|
|
execute cdback
|
|
endtry
|
|
execute delete . 'diffupdate'
|
|
call fugitive#ReloadStatus()
|
|
return 'redraw|echo '.string(':!'.git.' '.args)
|
|
endif
|
|
let [file, pre] = s:OpenParse(a:args)
|
|
try
|
|
let file = s:Generate(file)
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
if file =~# '^fugitive:' && after is# 0
|
|
return 'exe ' .string(mods . ' ' . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
|
|
endif
|
|
if foldlevel(after)
|
|
exe after . 'foldopen!'
|
|
endif
|
|
return mods . ' ' . after . 'read' . pre . ' ' . s:fnameescape(file) . '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')
|
|
endfunction
|
|
|
|
function! s:ReadComplete(A,L,P) abort
|
|
if a:L =~# '^\w\+!'
|
|
return fugitive#CompleteGit(a:A, a:L, a:P)
|
|
else
|
|
return fugitive#CompleteObject(a:A, a:L, a:P)
|
|
endif
|
|
endfunction
|
|
|
|
call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Ge execute s:Open('edit<bang>', 0, '<mods>', <q-args>, [<f-args>])")
|
|
call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gedit execute s:Open('edit<bang>', 0, '<mods>', <q-args>, [<f-args>])")
|
|
call s:command("-bar -bang -nargs=* -complete=customlist,s:ReadComplete Gpedit execute s:Open('pedit', <bang>0, '<mods>', <q-args>, [<f-args>])")
|
|
call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete Gsplit execute s:Open((<count> > 0 ? <count> : '').(<count> ? 'split' : 'edit'), <bang>0, '<mods>', <q-args>, [<f-args>])")
|
|
call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete Gvsplit execute s:Open((<count> > 0 ? <count> : '').(<count> ? 'vsplit' : 'edit!'), <bang>0, '<mods>', <q-args>, [<f-args>])")
|
|
call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete" . (has('patch-7.4.542') ? ' -addr=tabs' : '') . " Gtabedit execute s:Open((<count> >= 0 ? <count> : '').'tabedit', <bang>0, '<mods>', <q-args>, [<f-args>])")
|
|
call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete Gread", "Read")
|
|
|
|
" Section: :Gwrite, :Gwq
|
|
|
|
call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gwrite", "Write")
|
|
call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gw", "Write")
|
|
call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gwq", "Wq")
|
|
|
|
function! s:WriteCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
|
|
if exists('b:fugitive_commit_arguments')
|
|
return 'write|bdelete'
|
|
elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
|
|
return 'wq'
|
|
elseif get(b:, 'fugitive_type', '') ==# 'index'
|
|
return 'Gcommit'
|
|
elseif &buftype ==# 'nowrite' && getline(4) =~# '^+++ '
|
|
let filename = getline(4)[6:-1]
|
|
setlocal buftype=
|
|
silent write
|
|
setlocal buftype=nowrite
|
|
if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6]
|
|
let err = s:TreeChomp('apply', '--cached', '--reverse', '--', expand('%:p'))
|
|
else
|
|
let err = s:TreeChomp('apply', '--cached', '--', expand('%:p'))
|
|
endif
|
|
if err !=# ''
|
|
let v:errmsg = split(err,"\n")[0]
|
|
return 'echoerr v:errmsg'
|
|
elseif a:bang
|
|
return 'bdelete'
|
|
else
|
|
return 'Gedit '.fnameescape(filename)
|
|
endif
|
|
endif
|
|
let mytab = tabpagenr()
|
|
let mybufnr = bufnr('')
|
|
let file = len(a:args) ? s:Generate(s:Expand(join(a:args, ' '))) : fugitive#Real(@%)
|
|
if empty(file)
|
|
return 'echoerr '.string('fugitive: cannot determine file path')
|
|
endif
|
|
if file =~# '^fugitive:'
|
|
return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
|
|
endif
|
|
let always_permitted = s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^0\=$'
|
|
if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
|
|
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()
|
|
silent execute '%write '.temp
|
|
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
|
|
let did = 1
|
|
finally
|
|
if exists('restorewinnr')
|
|
wincmd p
|
|
endif
|
|
execute 'tabnext '.mytab
|
|
endtry
|
|
endif
|
|
endfor
|
|
endfor
|
|
if !exists('did')
|
|
call writefile(readfile(temp,'b'),file,'b')
|
|
endif
|
|
else
|
|
execute 'write! '.s:fnameescape(file)
|
|
endif
|
|
|
|
if a:bang
|
|
let error = s:TreeChomp('add', '--force', '--', file)
|
|
else
|
|
let error = s:TreeChomp('add', '--', file)
|
|
endif
|
|
if v:shell_error
|
|
let v:errmsg = 'fugitive: '.error
|
|
return 'echoerr v:errmsg'
|
|
endif
|
|
if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
|
|
setlocal nomodified
|
|
endif
|
|
|
|
let one = s:Generate(':1:'.file)
|
|
let two = s:Generate(':2:'.file)
|
|
let three = s:Generate(':3:'.file)
|
|
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
|
|
let zero = s:Generate(':0:'.file)
|
|
silent execute 'doautocmd BufWritePost' s:fnameescape(zero)
|
|
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
|
|
setlocal nomodified
|
|
diffupdate
|
|
finally
|
|
if exists('restorewinnr')
|
|
wincmd p
|
|
endif
|
|
execute 'tabnext '.mytab
|
|
endtry
|
|
break
|
|
endif
|
|
endfor
|
|
endfor
|
|
call fugitive#ReloadStatus()
|
|
return 'checktime'
|
|
endfunction
|
|
|
|
function! s:WqCommand(...) abort
|
|
let bang = a:5 ? '!' : ''
|
|
if exists('b:fugitive_commit_arguments')
|
|
return 'wq'.bang
|
|
endif
|
|
let result = call(s:function('s:WriteCommand'),a:000)
|
|
if result =~# '^\%(write\|wq\|echoerr\)'
|
|
return s:sub(result,'^write','wq')
|
|
else
|
|
return result.'|quit'.bang
|
|
endif
|
|
endfunction
|
|
|
|
augroup fugitive_commit
|
|
autocmd!
|
|
autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute s:sub(s:FinishCommit(), '^echoerr (.*)', 'echohl ErrorMsg|echo \1|echohl NONE')
|
|
augroup END
|
|
|
|
" Section: :Gpush, :Gfetch
|
|
|
|
call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gpush execute s:Dispatch('<bang>', 'push '.<q-args>)")
|
|
call s:command("-nargs=? -bang -complete=custom,s:RemoteComplete Gfetch execute s:Dispatch('<bang>', 'fetch '.<q-args>)")
|
|
|
|
function! s:Dispatch(bang, args)
|
|
let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
|
|
try
|
|
let cdback = s:Cd(s:Tree())
|
|
let b:current_compiler = 'git'
|
|
let &l:errorformat = s:common_efm
|
|
let &l:makeprg = substitute(s:UserCommand() . ' ' . a:args, '\s\+$', '', '')
|
|
if exists(':Make') == 2
|
|
Make
|
|
else
|
|
silent noautocmd make!
|
|
redraw!
|
|
return 'call fugitive#Cwindow()|call fugitive#ReloadStatus()'
|
|
endif
|
|
return ''
|
|
finally
|
|
let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
|
|
if empty(cc) | unlet! b:current_compiler | endif
|
|
execute cdback
|
|
endtry
|
|
endfunction
|
|
|
|
" Section: :Gdiff
|
|
|
|
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gdiff :execute s:Diff('',<bang>0,<f-args>)")
|
|
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gvdiff :execute s:Diff('keepalt vert ',<bang>0,<f-args>)")
|
|
call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gsdiff :execute s:Diff('keepalt ',<bang>0,<f-args>)")
|
|
|
|
augroup fugitive_diff
|
|
autocmd!
|
|
autocmd BufWinLeave *
|
|
\ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
|
|
\ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
|
|
\ endif
|
|
autocmd BufWinEnter *
|
|
\ 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(s:Dir(a:buf)) &&
|
|
\ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
|
|
endfunction
|
|
|
|
function! fugitive#CanDiffoff(buf) abort
|
|
return s:can_diffoff(bufnr(a:buf))
|
|
endfunction
|
|
|
|
function! s:diff_modifier(count) abort
|
|
let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
|
|
if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
|
|
return 'keepalt '
|
|
elseif &diffopt =~# 'vertical'
|
|
return 'keepalt vert '
|
|
elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
|
|
return 'keepalt '
|
|
else
|
|
return 'keepalt vert '
|
|
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:diff_restore() abort
|
|
let restore = 'setlocal nodiff noscrollbind'
|
|
\ . ' scrollopt=' . &l:scrollopt
|
|
\ . (&l:wrap ? ' wrap' : ' nowrap')
|
|
\ . ' foldlevel=999'
|
|
\ . ' foldmethod=' . &l:foldmethod
|
|
\ . ' foldcolumn=' . &l:foldcolumn
|
|
\ . ' foldlevel=' . &l:foldlevel
|
|
\ . (&l:foldenable ? ' foldenable' : ' nofoldenable')
|
|
if has('cursorbind')
|
|
let restore .= (&l:cursorbind ? ' ' : ' no') . 'cursorbind'
|
|
endif
|
|
return restore
|
|
endfunction
|
|
|
|
function! s:diffthis() abort
|
|
if !&diff
|
|
let w:fugitive_diff_restore = s:diff_restore()
|
|
diffthis
|
|
endif
|
|
endfunction
|
|
|
|
function! s:diffoff() abort
|
|
if exists('w:fugitive_diff_restore')
|
|
execute w:fugitive_diff_restore
|
|
unlet w:fugitive_diff_restore
|
|
else
|
|
diffoff
|
|
endif
|
|
endfunction
|
|
|
|
function! s:diffoff_all(dir) abort
|
|
let curwin = winnr()
|
|
for nr in range(1,winnr('$'))
|
|
if getwinvar(nr,'&diff')
|
|
if nr != winnr()
|
|
execute nr.'wincmd w'
|
|
let restorewinnr = 1
|
|
endif
|
|
if s:Dir() ==# a:dir
|
|
call s:diffoff()
|
|
endif
|
|
endif
|
|
endfor
|
|
execute curwin.'wincmd w'
|
|
endfunction
|
|
|
|
function! s:CompareAge(mine, theirs) abort
|
|
let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
|
|
let mine = substitute(a:mine, '^:', '', '')
|
|
let theirs = substitute(a:theirs, '^:', '', '')
|
|
let my_score = get(scores, ':'.mine, 0)
|
|
let their_score = get(scores, ':'.theirs, 0)
|
|
if my_score || their_score
|
|
return my_score < their_score ? -1 : my_score != their_score
|
|
elseif mine ==# theirs
|
|
return 0
|
|
endif
|
|
let base = s:TreeChomp('merge-base', mine, theirs)
|
|
if base ==# mine
|
|
return -1
|
|
elseif base ==# theirs
|
|
return 1
|
|
endif
|
|
let my_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:mine, '--')
|
|
let their_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:theirs, '--')
|
|
return my_time < their_time ? -1 : my_time != their_time
|
|
endfunction
|
|
|
|
function! s:Diff(vert,keepfocus,...) abort
|
|
let args = copy(a:000)
|
|
let post = ''
|
|
if get(args, 0) =~# '^+'
|
|
let post = remove(args, 0)[1:-1]
|
|
endif
|
|
let vert = empty(a:vert) ? s:diff_modifier(2) : a:vert
|
|
let commit = s:DirCommitFile(@%)[1]
|
|
let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
|
|
if exists(':DiffGitCached')
|
|
return 'DiffGitCached'
|
|
elseif (empty(args) || args[0] ==# ':') && commit =~# '^[0-1]\=$' && !empty(s:TreeChomp('ls-files', '--unmerged', '--', expand('%:p')))
|
|
if v:shell_error
|
|
return 'echoerr ' . string("fugitive: error determining merge status of the current buffer")
|
|
endif
|
|
let vert = empty(a:vert) ? s:diff_modifier(3) : a:vert
|
|
let nr = bufnr('')
|
|
execute 'leftabove '.vert.'split' s:fnameescape(s:Generate(s:Relative(':2:')))
|
|
execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
|
|
let nr2 = bufnr('')
|
|
call s:diffthis()
|
|
exe back
|
|
execute 'rightbelow '.vert.'split' s:fnameescape(s:Generate(s:Relative(':3:')))
|
|
execute 'nnoremap <buffer> <silent> dp :diffput '.nr.'<Bar>diffupdate<CR>'
|
|
let nr3 = bufnr('')
|
|
call s:diffthis()
|
|
exe back
|
|
call s:diffthis()
|
|
execute 'nnoremap <buffer> <silent> d2o :diffget '.nr2.'<Bar>diffupdate<CR>'
|
|
execute 'nnoremap <buffer> <silent> d3o :diffget '.nr3.'<Bar>diffupdate<CR>'
|
|
return post
|
|
elseif len(args)
|
|
let arg = join(args, ' ')
|
|
if arg ==# ''
|
|
return post
|
|
elseif arg ==# '/'
|
|
let file = s:Relative()
|
|
elseif arg ==# ':'
|
|
let file = s:Relative(':0:')
|
|
elseif arg =~# '^:/.'
|
|
try
|
|
let file = fugitive#RevParse(arg).s:Relative(':')
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
else
|
|
let file = s:Expand(arg)
|
|
endif
|
|
if file !~# ':' && file !~# '^/' && s:TreeChomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$'
|
|
let file = file.s:Relative(':')
|
|
endif
|
|
else
|
|
let file = empty(commit) ? s:Relative(':0:') : s:Relative()
|
|
endif
|
|
try
|
|
let spec = s:Generate(file)
|
|
let restore = s:diff_restore()
|
|
if exists('+cursorbind')
|
|
setlocal cursorbind
|
|
endif
|
|
let w:fugitive_diff_restore = restore
|
|
if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
|
|
execute 'rightbelow '.vert.'diffsplit '.s:fnameescape(spec)
|
|
else
|
|
execute 'leftabove '.vert.'diffsplit '.s:fnameescape(spec)
|
|
endif
|
|
let &l:readonly = &l:readonly
|
|
redraw
|
|
let w:fugitive_diff_restore = restore
|
|
let winnr = winnr()
|
|
if getwinvar('#', '&diff')
|
|
exe back
|
|
if !a:keepfocus
|
|
call feedkeys(winnr."\<C-W>w", 'n')
|
|
endif
|
|
endif
|
|
return post
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
endfunction
|
|
|
|
" Section: :Gmove, :Gremove
|
|
|
|
function! s:Move(force, rename, destination) abort
|
|
if a:destination =~# '^\.\.\=\%(/\|$\)'
|
|
let destination = simplify(getcwd() . '/' . a:destination)
|
|
elseif a:destination =~# '^\a\+:\|^/'
|
|
let destination = a:destination
|
|
elseif a:destination =~# '^:/:\='
|
|
let destination = s:Tree() . substitute(a:destination, '^:/:\=', '', '')
|
|
elseif a:destination =~# '^:(\%(top\|top,literal\|literal,top\))'
|
|
let destination = s:Tree() . matchstr(a:destination, ')\zs.*')
|
|
elseif a:destination =~# '^:(literal)'
|
|
let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
|
|
elseif a:rename
|
|
let destination = expand('%:p:s?[\/]$??:h') . '/' . a:destination
|
|
else
|
|
let destination = s:Tree() . '/' . a:destination
|
|
endif
|
|
let destination = s:Slash(destination)
|
|
if isdirectory(@%)
|
|
setlocal noswapfile
|
|
endif
|
|
let message = call('s:TreeChomp', ['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination])
|
|
if v:shell_error
|
|
let v:errmsg = 'fugitive: '.message
|
|
return 'echoerr v:errmsg'
|
|
endif
|
|
if isdirectory(destination)
|
|
let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
|
|
endif
|
|
call fugitive#ReloadStatus()
|
|
if empty(s:DirCommitFile(@%)[1])
|
|
if isdirectory(destination)
|
|
return 'keepalt edit '.s:fnameescape(destination)
|
|
else
|
|
return 'keepalt saveas! '.s:fnameescape(destination)
|
|
endif
|
|
else
|
|
return 'file '.s:fnameescape(s:Generate(':0:'.destination))
|
|
endif
|
|
endfunction
|
|
|
|
function! s:RenameComplete(A,L,P) abort
|
|
if a:A =~# '^[.:]\=/'
|
|
return fugitive#CompletePath(a:A)
|
|
else
|
|
let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
|
|
return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
|
|
endif
|
|
endfunction
|
|
|
|
function! s:Remove(after, force) abort
|
|
if s:DirCommitFile(@%)[1] ==# ''
|
|
let cmd = ['rm']
|
|
elseif s:DirCommitFile(@%)[1] ==# '0'
|
|
let cmd = ['rm','--cached']
|
|
else
|
|
let v:errmsg = 'fugitive: rm not supported here'
|
|
return 'echoerr v:errmsg'
|
|
endif
|
|
if a:force
|
|
let cmd += ['--force']
|
|
endif
|
|
let message = call('s:TreeChomp', cmd + ['--', expand('%:p')])
|
|
if v:shell_error
|
|
let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
|
|
return 'echoerr '.string(v:errmsg)
|
|
else
|
|
call fugitive#ReloadStatus()
|
|
return a:after . (a:force ? '!' : '')
|
|
endif
|
|
endfunction
|
|
|
|
augroup fugitive_remove
|
|
autocmd!
|
|
autocmd User Fugitive if s:DirCommitFile(@%)[1] =~# '^0\=$' |
|
|
\ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,fugitive#CompletePath Gmove :execute s:Move(<bang>0,0,<q-args>)" |
|
|
\ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:RenameComplete Grename :execute s:Move(<bang>0,1,<q-args>)" |
|
|
\ exe "command! -buffer -bar -bang Gremove :execute s:Remove('edit',<bang>0)" |
|
|
\ exe "command! -buffer -bar -bang Gdelete :execute s:Remove('bdelete',<bang>0)" |
|
|
\ endif
|
|
augroup END
|
|
|
|
" Section: :Gblame
|
|
|
|
function! s:Keywordprg() abort
|
|
let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
|
|
if has('gui_running') && !has('win32')
|
|
return s:UserCommand() . ' --no-pager' . args . ' log -1'
|
|
else
|
|
return s:UserCommand() . args . ' show'
|
|
endif
|
|
endfunction
|
|
|
|
augroup fugitive_blame
|
|
autocmd!
|
|
autocmd FileType fugitiveblame setlocal nomodeline | if len(s:Dir()) | let &l:keywordprg = s:Keywordprg() | endif
|
|
autocmd Syntax fugitiveblame call s:BlameSyntax()
|
|
autocmd User Fugitive
|
|
\ if get(b:, 'fugitive_type') =~# '^\%(file\|blob\|blame\)$' || filereadable(@%) |
|
|
\ exe "command! -buffer -bar -bang -range=0 -nargs=* Gblame :execute s:BlameCommand(<line1>,<line2>,+'<range>',<count>,<bang>0,'<mods>',<q-reg>,<q-args>,[<f-args>])" |
|
|
\ endif
|
|
autocmd ColorScheme,GUIEnter * call s:RehighlightBlame()
|
|
autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
|
|
augroup END
|
|
|
|
function! s:linechars(pattern) abort
|
|
let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
|
|
if exists('*synconcealed') && &conceallevel > 1
|
|
for col in range(1, chars)
|
|
let chars -= synconcealed(line('.'), col)[0]
|
|
endfor
|
|
endif
|
|
return chars
|
|
endfunction
|
|
|
|
function! s:BlameCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
|
|
if exists('b:fugitive_blamed_bufnr')
|
|
return 'bdelete'
|
|
endif
|
|
try
|
|
if empty(s:Relative('/'))
|
|
call s:throw('file or blob required')
|
|
endif
|
|
if filter(copy(a:args),'v:val !~# "^\\%(--abbrev=\\d*\\|--relative-date\\|--first-parent\\|--root\\|--show-name\\|-\\=\\%([ltfnsew]\\|[MC]\\d*\\)\\+\\)$"') != []
|
|
call s:throw('unsupported option')
|
|
endif
|
|
call map(a:args,'s:sub(v:val,"^\\ze[^-]","-")')
|
|
let cmd = ['--no-pager', 'blame', '--show-number']
|
|
if a:count
|
|
let cmd += ['-L', a:line1 . ',' . a:line1]
|
|
endif
|
|
let cmd += a:args
|
|
if s:DirCommitFile(@%)[1] =~# '\D\|..'
|
|
let cmd += [s:DirCommitFile(@%)[1]]
|
|
else
|
|
let cmd += ['--contents', '-']
|
|
endif
|
|
let cmd += ['--', expand('%:p')]
|
|
let basecmd = escape(fugitive#Prepare(cmd), '!#%')
|
|
try
|
|
let cdback = s:Cd(s:Tree())
|
|
let error = tempname()
|
|
let temp = error.'.fugitiveblame'
|
|
if &shell =~# 'csh'
|
|
silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
|
|
else
|
|
silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
|
|
endif
|
|
finally
|
|
execute cdback
|
|
endtry
|
|
try
|
|
if v:shell_error
|
|
call s:throw(join(readfile(error),"\n"))
|
|
endif
|
|
if a:count
|
|
let edit = substitute(a:mods, '^<mods>$', '', '') . get(['edit', 'split', 'pedit'], a:line2 - a:line1, ' split')
|
|
return s:BlameCommit(edit, get(readfile(temp), 0, ''))
|
|
else
|
|
for winnr in range(winnr('$'),1,-1)
|
|
call setwinvar(winnr, '&scrollbind', 0)
|
|
if exists('+cursorbind')
|
|
call setwinvar(winnr, '&cursorbind', 0)
|
|
endif
|
|
if getbufvar(winbufnr(winnr), 'fugitive_blamed_bufnr')
|
|
execute winbufnr(winnr).'bdelete'
|
|
endif
|
|
endfor
|
|
let bufnr = bufnr('')
|
|
let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
|
|
if exists('+cursorbind')
|
|
let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
|
|
endif
|
|
if &l:wrap
|
|
let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
|
|
endif
|
|
if &l:foldenable
|
|
let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
|
|
endif
|
|
setlocal scrollbind nowrap nofoldenable
|
|
if exists('+cursorbind')
|
|
setlocal cursorbind
|
|
endif
|
|
let top = line('w0') + &scrolloff
|
|
let current = line('.')
|
|
let temp = s:Resolve(temp)
|
|
let s:temp_files[s:cpath(temp)] = { 'dir': s:Dir(), 'filetype': 'fugitiveblame', 'args': cmd, 'bufnr': bufnr }
|
|
exe 'keepalt leftabove vsplit '.temp
|
|
let b:fugitive_blamed_bufnr = bufnr
|
|
let b:fugitive_type = 'blame'
|
|
let w:fugitive_leave = restore
|
|
let b:fugitive_blame_arguments = join(a:args,' ')
|
|
execute top
|
|
normal! zt
|
|
execute current
|
|
if exists('+cursorbind')
|
|
setlocal cursorbind
|
|
endif
|
|
setlocal nomodified nomodifiable nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth filetype=fugitiveblame buftype=nowrite
|
|
if exists('+concealcursor')
|
|
setlocal concealcursor=nc conceallevel=2
|
|
endif
|
|
if exists('+relativenumber')
|
|
setlocal norelativenumber
|
|
endif
|
|
execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
|
|
let nowait = v:version >= 704 ? '<nowait>' : ''
|
|
nnoremap <buffer> <silent> <F1> :help fugitive-:Gblame<CR>
|
|
nnoremap <buffer> <silent> g? :help fugitive-:Gblame<CR>
|
|
nnoremap <buffer> <silent> q :exe substitute(bufwinnr(b:fugitive_blamed_bufnr).' wincmd w<Bar>'.bufnr('').'bdelete','^-1','','')<CR>
|
|
exe 'nnoremap <buffer> <silent>' nowait "gq :exe substitute(bufwinnr(b:fugitive_blamed_bufnr).' wincmd w<Bar>'.bufnr('').'bdelete<Bar>if expand(''%:p'') =~# ''^fugitive:[\\/][\\/]''<Bar>Gedit<Bar>endif','^-1','','')<CR>"
|
|
nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>BlameCommit("exe 'norm q'<Bar>edit")<CR>
|
|
nnoremap <buffer> <silent> - :<C-U>exe <SID>BlameJump('')<CR>
|
|
nnoremap <buffer> <silent> P :<C-U>exe <SID>BlameJump('^'.v:count1)<CR>
|
|
nnoremap <buffer> <silent> ~ :<C-U>exe <SID>BlameJump('~'.v:count1)<CR>
|
|
nnoremap <buffer> <silent> i :<C-U>exe <SID>BlameCommit("exe 'norm q'<Bar>edit")<CR>
|
|
nnoremap <buffer> <silent> o :<C-U>exe <SID>BlameCommit((&splitbelow ? "botright" : "topleft")." split")<CR>
|
|
nnoremap <buffer> <silent> O :<C-U>exe <SID>BlameCommit("tabedit")<CR>
|
|
nnoremap <buffer> <silent> p :<C-U>exe <SID>Open((&splitbelow ? "botright" : "topleft").' pedit', 0, '', matchstr(getline('.'), '\x\+'), [matchstr(getline('.'), '\x\+')])<CR>
|
|
nnoremap <buffer> <silent> A :<C-u>exe "vertical resize ".(<SID>linechars('.\{-\}\ze [0-9:/+-][0-9:/+ -]* \d\+)')+1+v:count)<CR>
|
|
nnoremap <buffer> <silent> C :<C-u>exe "vertical resize ".(<SID>linechars('^\S\+')+1+v:count)<CR>
|
|
nnoremap <buffer> <silent> D :<C-u>exe "vertical resize ".(<SID>linechars('.\{-\}\ze\d\ze\s\+\d\+)')+1-v:count)<CR>
|
|
redraw
|
|
syncbind
|
|
endif
|
|
endtry
|
|
return ''
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:BlameCommit(cmd, ...) abort
|
|
let line = a:0 ? a:1 : getline('.')
|
|
if line =~# '^0\{4,40\} '
|
|
return 'echoerr ' . string('Not Committed Yet')
|
|
endif
|
|
let cmd = s:Open(a:cmd, 0, '', matchstr(line, '\x\+'), [matchstr(line, '\x\+')])
|
|
if cmd =~# '^echoerr'
|
|
return cmd
|
|
endif
|
|
let lnum = matchstr(line, ' \zs\d\+\ze\s\+[([:digit:]]')
|
|
let path = matchstr(line, '^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
|
|
if path ==# ''
|
|
let path = fugitive#Path(a:0 ? @% : bufname(b:fugitive_blamed_bufnr), '')
|
|
endif
|
|
execute cmd
|
|
if a:cmd ==# 'pedit'
|
|
return ''
|
|
endif
|
|
if search('^diff .* b/\M'.escape(path,'\').'$','W')
|
|
call search('^+++')
|
|
let head = line('.')
|
|
while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
|
|
let top = +matchstr(getline('.'),' +\zs\d\+')
|
|
let len = +matchstr(getline('.'),' +\d\+,\zs\d\+')
|
|
if lnum >= top && lnum <= top + len
|
|
let offset = lnum - top
|
|
if &scrolloff
|
|
+
|
|
normal! zt
|
|
else
|
|
normal! zt
|
|
+
|
|
endif
|
|
while offset > 0 && line('.') < line('$')
|
|
+
|
|
if getline('.') =~# '^[ +]'
|
|
let offset -= 1
|
|
endif
|
|
endwhile
|
|
return 'normal! zv'
|
|
endif
|
|
endwhile
|
|
execute head
|
|
normal! zt
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
function! s:BlameJump(suffix) abort
|
|
let commit = matchstr(getline('.'),'^\^\=\zs\x\+')
|
|
let suffix = a:suffix
|
|
if commit =~# '^0\+$'
|
|
let commit = 'HEAD'
|
|
let suffix = ''
|
|
endif
|
|
let lnum = matchstr(getline('.'),' \zs\d\+\ze\s\+[([:digit:]]')
|
|
let path = matchstr(getline('.'),'^\^\=\x\+\s\+\zs.\{-\}\ze\s*\d\+ ')
|
|
if path ==# ''
|
|
let path = fugitive#Path(bufname(b:fugitive_blamed_bufnr), '')
|
|
endif
|
|
let args = b:fugitive_blame_arguments
|
|
let offset = line('.') - line('w0')
|
|
let bufnr = bufnr('%')
|
|
let winnr = bufwinnr(b:fugitive_blamed_bufnr)
|
|
if winnr > 0
|
|
exe winnr.'wincmd w'
|
|
endif
|
|
execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
|
|
execute lnum
|
|
if winnr > 0
|
|
exe bufnr.'bdelete'
|
|
endif
|
|
if exists(':Gblame')
|
|
execute 'Gblame '.args
|
|
execute lnum
|
|
let delta = line('.') - line('w0') - offset
|
|
if delta > 0
|
|
execute 'normal! '.delta."\<C-E>"
|
|
elseif delta < 0
|
|
execute 'normal! '.(-delta)."\<C-Y>"
|
|
endif
|
|
syncbind
|
|
endif
|
|
return ''
|
|
endfunction
|
|
|
|
let s:hash_colors = {}
|
|
|
|
function! s:BlameSyntax() abort
|
|
let b:current_syntax = 'fugitiveblame'
|
|
let conceal = has('conceal') ? ' conceal' : ''
|
|
let arg = exists('b:fugitive_blame_arguments') ? b:fugitive_blame_arguments : ''
|
|
syn match FugitiveblameBoundary "^\^"
|
|
syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,fugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
|
|
syn match FugitiveblameHash "\%(^\^\=\)\@<=\<\x\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite
|
|
syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,40\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite
|
|
syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%( \d\+\)\@<=)" contained keepend oneline
|
|
syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%( \+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
|
|
exec 'syn match FugitiveblameLineNumber " *\d\+)\@=" contained containedin=FugitiveblameAnnotation'.conceal
|
|
exec 'syn match FugitiveblameOriginalFile " \%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite'.(arg =~# 'f' ? '' : conceal)
|
|
exec 'syn match FugitiveblameOriginalLineNumber " *\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite'.(arg =~# 'n' ? '' : conceal)
|
|
exec 'syn match FugitiveblameOriginalLineNumber " *\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite'.(arg =~# 'n' ? '' : conceal)
|
|
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
|
|
hi def link FugitiveblameUncommitted Ignore
|
|
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
|
|
let seen = {}
|
|
for lnum in range(1, line('$'))
|
|
let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
|
|
if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
|
|
continue
|
|
endif
|
|
let seen[hash] = 1
|
|
if &t_Co > 16 && get(g:, 'CSApprox_loaded') && !empty(findfile('autoload/csapprox/per_component.vim', escape(&rtp, ' ')))
|
|
\ && empty(get(s:hash_colors, hash))
|
|
let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
|
|
let color = csapprox#per_component#Approximate(r, g, b)
|
|
if color == 16 && &background ==# 'dark'
|
|
let color = 8
|
|
endif
|
|
let s:hash_colors[hash] = ' ctermfg='.color
|
|
else
|
|
let s:hash_colors[hash] = ''
|
|
endif
|
|
exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
|
|
endfor
|
|
call s:RehighlightBlame()
|
|
endfunction
|
|
|
|
function! s:RehighlightBlame() abort
|
|
for [hash, cterm] in items(s:hash_colors)
|
|
if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
|
|
exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
|
|
else
|
|
exe 'hi link FugitiveblameHash'.hash.' Identifier'
|
|
endif
|
|
endfor
|
|
endfunction
|
|
|
|
" Section: :Gbrowse
|
|
|
|
call s:command("-bar -bang -range=0 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse", "Browse")
|
|
|
|
let s:redirects = {}
|
|
|
|
function! s:BrowseCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
|
|
let dir = s:Dir()
|
|
try
|
|
let validremote = '\.\|\.\=/.*\|[[:alnum:]_-]\+\%(://.\{-\}\)\='
|
|
if len(a:args)
|
|
let remote = matchstr(join(a:args, ' '),'@\zs\%('.validremote.'\)$')
|
|
let rev = substitute(join(a:args, ' '),'@\%('.validremote.'\)$','','')
|
|
else
|
|
let remote = ''
|
|
let rev = ''
|
|
endif
|
|
if rev ==# ''
|
|
let rev = s:DirRev(@%)[1]
|
|
endif
|
|
if rev =~# '^:\=$'
|
|
let expanded = s:Relative()
|
|
else
|
|
let expanded = s:Expand(rev)
|
|
endif
|
|
let cdir = fugitive#CommonDir(s:Dir())
|
|
for subdir in ['tags/', 'heads/', 'remotes/']
|
|
if expanded !~# '^[./]' && filereadable(cdir . '/refs/' . subdir . expanded)
|
|
let expanded = '.git/refs/' . subdir . expanded
|
|
endif
|
|
endfor
|
|
let full = s:Generate(expanded)
|
|
let commit = ''
|
|
if full =~? '^fugitive:'
|
|
let [pathdir, commit, path] = s:DirCommitFile(full)
|
|
if commit =~# '^:\=\d$'
|
|
let commit = ''
|
|
endif
|
|
if commit =~ '..'
|
|
let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':'))
|
|
let branch = matchstr(expanded, '^[^:]*')
|
|
else
|
|
let type = 'blob'
|
|
endif
|
|
let path = path[1:-1]
|
|
elseif empty(s:Tree(dir))
|
|
let path = '.git/' . full[strlen(dir)+1:-1]
|
|
let type = ''
|
|
else
|
|
let path = full[strlen(s:Tree(dir))+1:-1]
|
|
if path =~# '^\.git/'
|
|
let type = ''
|
|
elseif isdirectory(full)
|
|
let type = 'tree'
|
|
else
|
|
let type = 'blob'
|
|
endif
|
|
endif
|
|
if type ==# 'tree' && !empty(path)
|
|
let path = s:sub(path, '/\=$', '/')
|
|
endif
|
|
if path =~# '^\.git/.*HEAD$' && filereadable(dir . '/' . path[5:-1])
|
|
let body = readfile(dir . '/' . path[5:-1])[0]
|
|
if body =~# '^\x\{40\}$'
|
|
let commit = body
|
|
let type = 'commit'
|
|
let path = ''
|
|
elseif body =~# '^ref: refs/'
|
|
let path = '.git/' . matchstr(body,'ref: \zs.*')
|
|
endif
|
|
endif
|
|
|
|
let merge = ''
|
|
if path =~# '^\.git/refs/remotes/.'
|
|
if empty(remote)
|
|
let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+')
|
|
let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
|
|
else
|
|
let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
|
|
let path = '.git/refs/heads/'.merge
|
|
endif
|
|
elseif path =~# '^\.git/refs/heads/.'
|
|
let branch = path[16:-1]
|
|
elseif !exists('branch')
|
|
let branch = FugitiveHead()
|
|
endif
|
|
if !empty(branch)
|
|
let r = fugitive#Config('branch.'.branch.'.remote')
|
|
let m = fugitive#Config('branch.'.branch.'.merge')[11:-1]
|
|
if r ==# '.' && !empty(m)
|
|
let r2 = fugitive#Config('branch.'.m.'.remote')
|
|
if r2 !~# '^\.\=$'
|
|
let r = r2
|
|
let m = fugitive#Config('branch.'.m.'.merge')[11:-1]
|
|
endif
|
|
endif
|
|
if empty(remote)
|
|
let remote = r
|
|
endif
|
|
if r ==# '.' || r ==# remote
|
|
let merge = m
|
|
if path =~# '^\.git/refs/heads/.'
|
|
let path = '.git/refs/heads/'.merge
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
let line1 = a:count > 0 ? a:line1 : 0
|
|
let line2 = a:count > 0 ? a:count : 0
|
|
if empty(commit) && path !~# '^\.git/'
|
|
if a:line1 && !a:count && !empty(merge)
|
|
let commit = merge
|
|
else
|
|
let commit = ''
|
|
if len(merge)
|
|
let owner = s:Owner(@%)
|
|
let commit = s:TreeChomp('merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? 'HEAD' : owner, '--')
|
|
if v:shell_error
|
|
let commit = ''
|
|
endif
|
|
if a:count && empty(a:args) && commit =~# '^\x\{40\}$'
|
|
let blame_list = tempname()
|
|
call writefile([commit, ''], blame_list, 'b')
|
|
let blame_in = tempname()
|
|
silent exe '%write' blame_in
|
|
let blame = split(s:TreeChomp('blame', '--contents', blame_in, '-L', a:line1.','.a:count, '-S', blame_list, '-s', '--show-number', './' . path), "\n")
|
|
if !v:shell_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
|
|
call s:throw("Can't browse to uncommitted change")
|
|
endif
|
|
endif
|
|
endif
|
|
endif
|
|
endif
|
|
if empty(commit)
|
|
let commit = readfile(dir . '/HEAD', '', 1)[0]
|
|
endif
|
|
let i = 0
|
|
while commit =~# '^ref: ' && i < 10
|
|
let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
|
|
let i -= 1
|
|
endwhile
|
|
endif
|
|
|
|
if empty(remote)
|
|
let remote = '.'
|
|
endif
|
|
let raw = fugitive#RemoteUrl(remote)
|
|
if empty(raw)
|
|
let raw = remote
|
|
endif
|
|
|
|
if raw =~# '^https\=://' && s:executable('curl')
|
|
if !has_key(s:redirects, raw)
|
|
let s:redirects[raw] = matchstr(system('curl -I ' .
|
|
\ s:shellesc(raw . '/info/refs?service=git-upload-pack')),
|
|
\ 'Location: \zs\S\+\ze/info/refs?')
|
|
endif
|
|
if len(s:redirects[raw])
|
|
let raw = s:redirects[raw]
|
|
endif
|
|
endif
|
|
|
|
let opts = {
|
|
\ 'dir': dir,
|
|
\ 'repo': fugitive#repo(dir),
|
|
\ 'remote': raw,
|
|
\ 'revision': 'No longer provided',
|
|
\ 'commit': commit,
|
|
\ 'path': path,
|
|
\ 'type': type,
|
|
\ 'line1': line1,
|
|
\ 'line2': line2}
|
|
|
|
let url = ''
|
|
for Handler in get(g:, 'fugitive_browse_handlers', [])
|
|
let url = call(Handler, [copy(opts)])
|
|
if !empty(url)
|
|
break
|
|
endif
|
|
endfor
|
|
|
|
if empty(url)
|
|
call s:throw("No Gbrowse handler found for '".raw."'")
|
|
endif
|
|
|
|
let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))')
|
|
if a:bang
|
|
if has('clipboard')
|
|
let @+ = url
|
|
endif
|
|
return 'echomsg '.string(url)
|
|
elseif exists(':Browse') == 2
|
|
return 'echomsg '.string(url).'|Browse '.url
|
|
else
|
|
if !exists('g:loaded_netrw')
|
|
runtime! autoload/netrw.vim
|
|
endif
|
|
if exists('*netrw#BrowseX')
|
|
return 'echomsg '.string(url).'|call netrw#BrowseX('.string(url).', 0)'
|
|
else
|
|
return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
|
|
endif
|
|
endif
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
endfunction
|
|
|
|
" Section: Go to file
|
|
|
|
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')
|
|
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)
|
|
call s:map('c', '<C-R><C-F>', '<Plug><cfile>', '<silent><unique>', 1)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:ContainingCommit() abort
|
|
let commit = s:Owner(@%)
|
|
return empty(commit) ? 'HEAD' : commit
|
|
endfunction
|
|
|
|
function! s:SquashArgument() abort
|
|
if &filetype == 'fugitive'
|
|
return matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze ')
|
|
else
|
|
return s:Owner(@%)
|
|
endif
|
|
endfunction
|
|
|
|
function! s:NavigateUp(count) abort
|
|
let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
|
|
let c = a:count
|
|
while c
|
|
if rev =~# ':.*/.'
|
|
let rev = matchstr(rev, '.*\ze/.\+', '')
|
|
elseif rev =~# '.:.'
|
|
let rev = matchstr(rev, '^.[^:]*:')
|
|
elseif rev =~# '^:'
|
|
let rev = 'HEAD^{}'
|
|
elseif rev =~# ':$'
|
|
let rev = rev[0:-2]
|
|
else
|
|
return rev.'~'.c
|
|
endif
|
|
let c -= 1
|
|
endwhile
|
|
return rev
|
|
endfunction
|
|
|
|
function! fugitive#MapJumps(...) abort
|
|
if get(b:, 'fugitive_type', '') ==# 'blob'
|
|
nnoremap <buffer> <silent> <CR> :<C-U>.Gblame<CR>
|
|
else
|
|
nnoremap <buffer> <silent> <CR> :<C-U>exe <SID>GF("edit")<CR>
|
|
endif
|
|
if !&modifiable
|
|
let nowait = v:version >= 704 ? '<nowait>' : ''
|
|
if get(b:, 'fugitive_type', '') ==# 'blob'
|
|
nnoremap <buffer> <silent> o :<C-U>.,.+1Gblame<CR>
|
|
nnoremap <buffer> <silent> S :<C-U>echoerr 'Use gO'<CR>
|
|
nnoremap <buffer> <silent> gO :<C-U>vertical .,.+1Gblame<CR>
|
|
nnoremap <buffer> <silent> O :<C-U>tab .,.+1Gblame<CR>
|
|
nnoremap <buffer> <silent> p :<C-U>.,.+2Gblame<CR>
|
|
else
|
|
nnoremap <buffer> <silent> o :<C-U>exe <SID>GF("split")<CR>
|
|
nnoremap <buffer> <silent> S :<C-U>echoerr 'Use gO'<CR>
|
|
nnoremap <buffer> <silent> gO :<C-U>exe <SID>GF("vsplit")<CR>
|
|
nnoremap <buffer> <silent> O :<C-U>exe <SID>GF("tabedit")<CR>
|
|
nnoremap <buffer> <silent> p :<C-U>exe <SID>GF("pedit")<CR>
|
|
endif
|
|
exe "nnoremap <buffer> <silent>" nowait "- :<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>"
|
|
nnoremap <buffer> <silent> P :<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>
|
|
nnoremap <buffer> <silent> ~ :<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>
|
|
nnoremap <buffer> <silent> C :<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>
|
|
nnoremap <buffer> <silent> co :<C-U>echoerr 'Use CTRL-W C'<CR>
|
|
nnoremap <buffer> <silent> <C-W>C :<C-U>exe 'Gsplit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>
|
|
nnoremap <buffer> <silent> cp :<C-U>echoerr 'Use gC'<CR>
|
|
nnoremap <buffer> <silent> gC :<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>
|
|
nnoremap <buffer> <silent> gc :<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>
|
|
nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
|
|
nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
|
|
nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
|
|
nnoremap <buffer> <silent> cw :<C-U>Gcommit --amend --only<CR>
|
|
nnoremap <buffer> <silent> cva :<C-U>Gcommit -v --amend<CR>
|
|
nnoremap <buffer> <silent> cvc :<C-U>Gcommit -v<CR>
|
|
nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
|
|
nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
|
|
nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
|
|
nnoremap <buffer> <silent> ri :<C-U>Grebase --interactive<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR><CR>
|
|
nnoremap <buffer> <silent> rf :<C-U>Grebase --autosquash<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR><CR>
|
|
nnoremap <buffer> <silent> ru :<C-U>Grebase --interactive @{upstream}<CR>
|
|
nnoremap <buffer> <silent> rp :<C-U>Grebase --interactive @{push}<CR>
|
|
nnoremap <buffer> <silent> rw :<C-U>exe 'Grebase --interactive<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR>'<Bar>s/^pick/reword/e<CR>
|
|
nnoremap <buffer> <silent> rm :<C-U>exe 'Grebase --interactive<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR>'<Bar>s/^pick/edit/e<CR>
|
|
nnoremap <buffer> <silent> rd :<C-U>exe 'Grebase --interactive<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR>'<Bar>s/^pick/drop/e<CR>
|
|
nnoremap <buffer> <silent> rk :<C-U>exe 'Grebase --interactive<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR>'<Bar>s/^pick/drop/e<CR>
|
|
nnoremap <buffer> <silent> rx :<C-U>exe 'Grebase --interactive<C-R>=substitute(<SID>SquashArgument(),'.\+',' &^','')<CR>'<Bar>s/^pick/drop/e<CR>
|
|
nnoremap <buffer> <silent> rr :<C-U>Grebase --continue<CR>
|
|
nnoremap <buffer> <silent> rs :<C-U>Grebase --skip<CR>
|
|
nnoremap <buffer> <silent> re :<C-U>Grebase --edit-todo<CR>
|
|
nnoremap <buffer> <silent> ra :<C-U>Grebase --abort<CR>
|
|
nnoremap <buffer> . :<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>
|
|
xnoremap <buffer> . :<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>
|
|
nnoremap <buffer> <silent> g? :help fugitive-mappings<CR>
|
|
endif
|
|
endfunction
|
|
|
|
function! s:StatusCfile(...) abort
|
|
let tree = s:Tree()
|
|
let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
|
|
let info = s:StageInfo()
|
|
let line = getline('.')
|
|
if len(info.sigil) && len(info.section) && len(info.filename)
|
|
if info.section ==# 'Unstaged' && info.sigil !=# '-'
|
|
return [lead . info.filename, info.offset, 'normal!zv']
|
|
elseif info.section ==# 'Staged' && info.sigil ==# '-'
|
|
return ['@:' . info.filename, info.offset, 'normal!zv']
|
|
else
|
|
return [':0:' . info.filename, info.offset, 'normal!zv']
|
|
endif
|
|
elseif len(info.filename)
|
|
return [lead . info.filename]
|
|
elseif len(info.commit)
|
|
return [info.commit]
|
|
elseif line =~# '^\%(Head\|Merge\|Rebase\|Upstream\|Pull\|Push\): '
|
|
return [matchstr(line, ' \zs.*')]
|
|
else
|
|
return ['']
|
|
endif
|
|
endfunction
|
|
|
|
function! fugitive#StatusCfile() abort
|
|
let file = s:Generate(s:StatusCfile()[0])
|
|
return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
|
|
endfunction
|
|
|
|
function! s:MessageCfile(...) abort
|
|
let tree = s:Tree()
|
|
let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
|
|
if getline('.') =~# '^.\=\trenamed:.* -> '
|
|
return lead . matchstr(getline('.'),' -> \zs.*')
|
|
elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
|
|
return lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
|
|
elseif getline('.') =~# '^.\=\t.'
|
|
return lead . matchstr(getline('.'),'\t\zs.*')
|
|
elseif getline('.') =~# ': needs merge$'
|
|
return lead . matchstr(getline('.'),'.*\ze: needs merge$')
|
|
elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
|
|
return 'HEAD'
|
|
elseif getline('.') =~# '^\%(. \)\=On branch '
|
|
return 'refs/heads/'.getline('.')[12:]
|
|
elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
|
|
return matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
|
|
else
|
|
return ''
|
|
endif
|
|
endfunction
|
|
|
|
function! fugitive#MessageCfile() abort
|
|
let file = s:Generate(s:MessageCfile())
|
|
return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
|
|
endfunction
|
|
|
|
function! s:cfile() abort
|
|
try
|
|
let myhash = s:DirRev(@%)[1]
|
|
if len(myhash)
|
|
try
|
|
let myhash = fugitive#RevParse(myhash)
|
|
catch /^fugitive:/
|
|
let myhash = ''
|
|
endtry
|
|
endif
|
|
if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w'
|
|
let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
|
|
endif
|
|
|
|
let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
|
|
|
|
let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
|
|
\ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
|
|
|
|
if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40\}\t'
|
|
return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
|
|
elseif showtree
|
|
return [treebase . s:sub(getline('.'),'/$','')]
|
|
|
|
else
|
|
|
|
let dcmds = []
|
|
|
|
" Index
|
|
if getline('.') =~# '^\d\{6\} \x\{40\} \d\t'
|
|
let ref = matchstr(getline('.'),'\x\{40\}')
|
|
let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
|
|
return [file]
|
|
endif
|
|
|
|
if getline('.') =~# '^ref: '
|
|
let ref = strpart(getline('.'),5)
|
|
|
|
elseif getline('.') =~# '^commit \x\{40\}\>'
|
|
let ref = matchstr(getline('.'),'\x\{40\}')
|
|
return [ref]
|
|
|
|
elseif getline('.') =~# '^parent \x\{40\}\>'
|
|
let ref = matchstr(getline('.'),'\x\{40\}')
|
|
let line = line('.')
|
|
let parent = 0
|
|
while getline(line) =~# '^parent '
|
|
let parent += 1
|
|
let line -= 1
|
|
endwhile
|
|
return [ref]
|
|
|
|
elseif getline('.') =~# '^tree \x\{40\}$'
|
|
let ref = matchstr(getline('.'),'\x\{40\}')
|
|
if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
|
|
let ref = myhash.':'
|
|
endif
|
|
return [ref]
|
|
|
|
elseif getline('.') =~# '^object \x\{40\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
|
|
let ref = matchstr(getline('.'),'\x\{40\}')
|
|
let type = matchstr(getline(line('.')+1),'type \zs.*')
|
|
|
|
elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
|
|
let ref = s:DirRev(@%)[1]
|
|
|
|
elseif getline('.') =~# '^\l\{3,8\} \x\{40\}\>'
|
|
let ref = matchstr(getline('.'),'\x\{40\}')
|
|
echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
|
|
|
|
elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
|
|
let ref = getline('.')[4:]
|
|
|
|
elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
|
|
let type = getline('.')[0]
|
|
let lnum = line('.') - 1
|
|
let offset = 0
|
|
while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
|
|
if getline(lnum) =~# '^[ '.type.']'
|
|
let offset += 1
|
|
endif
|
|
let lnum -= 1
|
|
endwhile
|
|
let offset += matchstr(getline(lnum), type.'\zs\d\+')
|
|
let ref = getline(search('^'.type.'\{3\} [abciow12]/','bnW'))[4:-1]
|
|
let dcmds = [offset, 'normal!zv']
|
|
|
|
elseif getline('.') =~# '^rename from '
|
|
let ref = 'a/'.getline('.')[12:]
|
|
elseif getline('.') =~# '^rename to '
|
|
let ref = 'b/'.getline('.')[10:]
|
|
|
|
elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
|
|
let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
|
|
let offset = matchstr(getline('.'), '+\zs\d\+')
|
|
|
|
let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
|
|
let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
|
|
let dcmd = 'Gdiff! +'.offset
|
|
|
|
elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
|
|
let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
|
|
let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
|
|
let dcmd = 'Gdiff!'
|
|
|
|
elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
|
|
let line = getline(line('.')-1)
|
|
let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
|
|
let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
|
|
let dcmd = 'Gdiff!'
|
|
|
|
elseif line('$') == 1 && getline('.') =~ '^\x\{40\}$'
|
|
let ref = getline('.')
|
|
|
|
elseif expand('<cword>') =~# '^\x\{7,40\}\>'
|
|
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
|
|
let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
|
|
if exists('dref')
|
|
let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
|
|
endif
|
|
|
|
if ref ==# '/dev/null'
|
|
" Empty blob
|
|
let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
|
|
endif
|
|
|
|
if exists('dref')
|
|
return [ref, dcmd . ' ' . s:fnameescape(dref)] + dcmds
|
|
elseif ref != ""
|
|
return [ref] + dcmds
|
|
endif
|
|
|
|
endif
|
|
return []
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:GF(mode) abort
|
|
try
|
|
let results = &filetype ==# 'fugitive' ? s:StatusCfile() : &filetype ==# 'gitcommit' ? [s:MessageCfile()] : s:cfile()
|
|
catch /^fugitive:/
|
|
return 'echoerr v:errmsg'
|
|
endtry
|
|
if len(results) > 1
|
|
return 'G' . a:mode .
|
|
\ ' +' . escape(join(results[1:-1], '|'), '| ') . ' ' .
|
|
\ s:fnameescape(results[0])
|
|
elseif len(results) && len(results[0])
|
|
return 'G' . a:mode . ' ' . s:fnameescape(results[0])
|
|
else
|
|
return ''
|
|
endif
|
|
endfunction
|
|
|
|
function! fugitive#Cfile() abort
|
|
let pre = ''
|
|
let results = s:cfile()
|
|
if empty(results)
|
|
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
|
|
return pre . s:fnameescape(s:Generate(results[0]))
|
|
endfunction
|
|
|
|
" Section: Statusline
|
|
|
|
function! fugitive#Statusline(...) abort
|
|
if empty(s:Dir())
|
|
return ''
|
|
endif
|
|
let status = ''
|
|
let commit = s:DirCommitFile(@%)[1]
|
|
if len(commit)
|
|
let status .= ':' . commit[0:6]
|
|
endif
|
|
let status .= '('.FugitiveHead(7).')'
|
|
return '[Git'.status.']'
|
|
endfunction
|
|
|
|
function! fugitive#statusline(...) abort
|
|
return fugitive#Statusline()
|
|
endfunction
|
|
|
|
function! fugitive#head(...) abort
|
|
if empty(s:Dir())
|
|
return ''
|
|
endif
|
|
|
|
return fugitive#Head(a:0 ? a:1 : 0)
|
|
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)
|
|
if filename ==# '' && line =~# '^[+-]\{3\} [abciow12]/'
|
|
let filename = line[6:-1]
|
|
endif
|
|
if line =~# '^+'
|
|
let add += 1
|
|
elseif line =~# '^-'
|
|
let remove += 1
|
|
elseif line =~# '^Binary '
|
|
let binary = 1
|
|
endif
|
|
endfor
|
|
if filename ==# ''
|
|
let filename = matchstr(line_foldstart, '^diff .\{-\} [abciow12]/\zs.*\ze [abciow12]/')
|
|
endif
|
|
if filename ==# ''
|
|
let filename = line_foldstart[5:-1]
|
|
endif
|
|
if exists('binary')
|
|
return 'Binary: '.filename
|
|
else
|
|
return (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
|
|
endif
|
|
elseif line_foldstart =~# '^# .*:$'
|
|
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
|
|
|
|
augroup fugitive_folding
|
|
autocmd!
|
|
autocmd User Fugitive
|
|
\ if &filetype =~# '^git\%(commit\)\=$' && &foldtext ==# 'foldtext()' |
|
|
\ set foldtext=fugitive#Foldtext() |
|
|
\ endif
|
|
augroup END
|
|
|
|
" Section: Initialization
|
|
|
|
function! fugitive#Init() abort
|
|
if exists('#User#FugitiveBoot')
|
|
try
|
|
let [save_mls, &modelines] = [&mls, 0]
|
|
doautocmd User FugitiveBoot
|
|
finally
|
|
let &mls = save_mls
|
|
endtry
|
|
endif
|
|
if !exists('g:fugitive_no_maps')
|
|
call s:map('c', '<C-R><C-G>', '<SID>fnameescape(fugitive#Object(@%))', '<expr>')
|
|
call s:map('n', 'y<C-G>', ':<C-U>call setreg(v:register, fugitive#Object(@%))<CR>', '<silent>')
|
|
endif
|
|
if expand('%:p') =~# ':[\/][\/]'
|
|
let &l:path = s:sub(&path, '^\.%(,|$)', '')
|
|
endif
|
|
let dir = s:Dir()
|
|
if stridx(&tags, escape(dir, ', ')) == -1
|
|
if filereadable(dir.'/tags')
|
|
let &l:tags = escape(dir.'/tags', ', ').','.&tags
|
|
endif
|
|
if &filetype !=# '' && filereadable(dir.'/'.&filetype.'.tags')
|
|
let &l:tags = escape(dir.'/'.&filetype.'.tags', ', ').','.&tags
|
|
endif
|
|
endif
|
|
try
|
|
let [save_mls, &modelines] = [&mls, 0]
|
|
call s:define_commands()
|
|
doautocmd User Fugitive
|
|
finally
|
|
let &mls = save_mls
|
|
endtry
|
|
endfunction
|
|
|
|
function! fugitive#is_git_dir(path) abort
|
|
return FugitiveIsGitDir(a:path)
|
|
endfunction
|
|
|
|
function! fugitive#extract_git_dir(path) abort
|
|
return FugitiveExtractGitDir(a:path)
|
|
endfunction
|
|
|
|
function! fugitive#detect(path) abort
|
|
return FugitiveDetect(a:path)
|
|
endfunction
|
|
|
|
" Section: End
|