1 " Location: autoload/fugitive.vim
2 " Maintainer: Tim Pope <http://tpo.pe/>
4 if exists('g:autoloaded_fugitive')
7 let g:autoloaded_fugitive = 1
9 if !exists('g:fugitive_git_executable')
10 let g:fugitive_git_executable = 'git'
11 elseif g:fugitive_git_executable =~# '^\w\+='
12 let g:fugitive_git_executable = 'env ' . g:fugitive_git_executable
17 function! s:function(name) abort
18 return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
21 function! s:sub(str,pat,rep) abort
22 return substitute(a:str,'\v\C'.a:pat,a:rep,'')
25 function! s:gsub(str,pat,rep) abort
26 return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
29 function! s:Uniq(list) abort
33 let str = string(a:list[i])
35 call remove(a:list, i)
44 function! s:winshell() abort
45 return has('win32') && &shellcmdflag !~# '^-'
48 function! s:shellesc(arg) abort
49 if type(a:arg) == type([])
50 return join(map(copy(a:arg), 's:shellesc(v:val)'))
51 elseif a:arg =~ '^[A-Za-z0-9_/:.-]\+$'
54 return '"'.s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"').'"'
56 return shellescape(a:arg)
60 let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
61 function! s:fnameescape(file) abort
62 if type(a:file) == type([])
63 return join(map(copy(a:file), 's:fnameescape(v:val)'))
64 elseif exists('*fnameescape')
65 return fnameescape(a:file)
67 return escape(a:file, s:fnameescape)
71 function! s:throw(string) abort
72 throw 'fugitive: '.a:string
75 function! s:DirCheck(...) abort
76 if !empty(a:0 ? s:Dir(a:1) : s:Dir())
78 elseif empty(bufname(''))
79 return 'return ' . string('echoerr "fugitive: blank buffer unsupported (edit a file from a repository)"')
81 return 'return ' . string('echoerr "fugitive: file does not belong to a Git repository"')
85 function! s:Mods(mods, ...) abort
86 let mods = substitute(a:mods, '\C<mods>', '', '')
87 let mods = mods =~# '\S$' ? mods . ' ' : mods
88 if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>'
89 let mods = a:1 . ' ' . mods
91 return substitute(mods, '\s\+', ' ', 'g')
94 function! s:Slash(path) abort
95 if exists('+shellslash')
96 return tr(a:path, '\', '/')
102 function! s:Resolve(path) abort
103 let path = resolve(a:path)
105 let path = FugitiveVimPath(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t'))
110 function! s:cpath(path, ...) abort
111 if exists('+fileignorecase') && &fileignorecase
112 let path = FugitiveVimPath(tolower(a:path))
114 let path = FugitiveVimPath(a:path)
116 return a:0 ? path ==# s:cpath(a:1) : path
119 function! s:Cd(...) abort
120 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'
128 exe cd s:fnameescape(a:1)
129 return cd . ' ' . s:fnameescape(cwd)
132 let s:executables = {}
134 function! s:executable(binary) abort
135 if !has_key(s:executables, a:binary)
136 let s:executables[a:binary] = executable(a:binary)
138 return s:executables[a:binary]
141 function! s:DoAutocmd(cmd) abort
142 if v:version >= 704 || (v:version == 703 && has('patch442'))
143 return 'doautocmd <nomodeline>' . a:cmd
144 elseif &modelines > 0
145 return 'try|set modelines=0|doautocmd ' . a:cmd . '|finally|set modelines=' . &modelines . '|endtry'
147 return 'doautocmd ' . a:cmd
151 let s:nowait = v:version >= 704 ? '<nowait>' : ''
153 function! s:Map(mode, lhs, rhs, ...) abort
154 for mode in split(a:mode, '\zs')
155 let flags = (a:0 ? a:1 : '') . (a:rhs =~# '<Plug>' ? '' : '<script>')
158 let keys = get(g:, mode.'remap', {})
159 if type(keys) == type([])
163 if has_key(keys, head)
164 let head = keys[head]
170 let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
171 let head = substitute(head, '<[^<>]*>$\|.$', '', '')
173 if flags !~# '<unique>' || empty(mapcheck(head.tail, mode))
174 exe mode.'map <buffer>' s:nowait flags head.tail a:rhs
176 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') .
177 \ '|sil! exe "' . mode . 'unmap <buffer> ' . head.tail . '"'
185 function! s:QuickfixGet(nr, ...) abort
187 return call('getqflist', a:000)
189 return call('getloclist', [a:nr] + a:000)
193 function! s:QuickfixSet(nr, ...) abort
195 return call('setqflist', a:000)
197 return call('setloclist', [a:nr] + a:000)
201 function! s:QuickfixCreate(nr, opts) abort
202 if has('patch-7.4.2200')
203 call s:QuickfixSet(a:nr, [], ' ', a:opts)
205 call s:QuickfixSet(a:nr, [], ' ')
209 function! s:QuickfixStream(nr, title, cmd, first, callback, ...) abort
210 call s:QuickfixCreate(a:nr, {'title': a:title})
212 exe a:nr < 0 ? 'copen' : 'lopen'
218 let lines = split(s:SystemError(s:shellesc(a:cmd))[0], "\n")
220 call extend(buffer, call(a:callback, a:000 + [line]))
222 call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
226 call s:QuickfixSet(a:nr, extend(buffer, call(a:callback, a:000 + [0])), 'a')
228 if a:first && len(s:QuickfixGet(a:nr))
230 return a:nr < 0 ? 'cfirst' : 'lfirst'
238 function! s:UserCommandList(...) abort
239 let git = split(get(g:, 'fugitive_git_command', g:fugitive_git_executable), '\s\+')
240 let dir = a:0 ? s:Dir(a:1) : ''
242 let tree = s:Tree(dir)
244 call add(git, '--git-dir=' . FugitiveGitPath(dir))
245 elseif len(tree) && s:cpath(tree) !=# s:cpath(getcwd())
246 if fugitive#GitVersion(1, 8, 5)
247 call extend(git, ['-C', FugitiveGitPath(tree)])
249 throw 'fugitive: Git 1.8.5 or higher required to change directory'
256 function! s:UserCommand(...) abort
257 return s:shellesc(call('s:UserCommandList', a:0 ? [a:1] : []) + (a:0 ? a:2 : []))
260 let s:git_versions = {}
261 function! fugitive#GitVersion(...) abort
262 if !has_key(s:git_versions, g:fugitive_git_executable)
263 let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), '\d[^[:space:]]\+')
266 return s:git_versions[g:fugitive_git_executable]
268 let components = split(s:git_versions[g:fugitive_git_executable], '\D\+')
272 for i in range(len(a:000))
273 if a:000[i] > +get(components, i)
275 elseif a:000[i] < +get(components, i)
279 return a:000[i] ==# get(components, i)
282 let s:commondirs = {}
283 function! fugitive#CommonDir(dir) abort
287 if !has_key(s:commondirs, a:dir)
288 if getfsize(a:dir . '/HEAD') < 10
289 let s:commondirs[a:dir] = ''
290 elseif filereadable(a:dir . '/commondir')
291 let cdir = get(readfile(a:dir . '/commondir', 1), 0, '')
292 if cdir =~# '^/\|^\a:/'
293 let s:commondirs[a:dir] = s:Slash(FugitiveVimPath(cdir))
295 let s:commondirs[a:dir] = simplify(a:dir . '/' . cdir)
298 let s:commondirs[a:dir] = a:dir
301 return s:commondirs[a:dir]
304 function! s:Dir(...) abort
305 return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
308 function! s:Tree(...) abort
309 return a:0 ? FugitiveWorkTree(a:1) : FugitiveWorkTree()
312 function! s:HasOpt(args, ...) abort
313 let args = a:args[0 : index(a:args, '--')]
314 let opts = copy(a:000)
315 if type(opts[0]) == type([])
316 if empty(args) || index(opts[0], args[0]) == -1
322 if index(args, opt) != -1
328 function! s:PreparePathArgs(cmd, dir, literal) abort
329 let literal_supported = fugitive#GitVersion(1, 9)
330 if a:literal && literal_supported
331 call insert(a:cmd, '--literal-pathspecs')
333 let split = index(a:cmd, '--')
334 for i in range(split < 0 ? len(a:cmd) : split)
335 if type(a:cmd[i]) == type(0)
336 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
342 for i in range(split + 1, len(a:cmd) - 1)
343 if type(a:cmd[i]) == type(0)
344 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
346 let a:cmd[i] = fugitive#Path(a:cmd[i], './', a:dir)
347 elseif !literal_supported
348 let a:cmd[i] = substitute(a:cmd[i], '^:\%(/\|([^)]*)\)\=:\=', './', '')
354 let s:prepare_env = {
355 \ 'sequence.editor': 'GIT_SEQUENCE_EDITOR',
356 \ 'core.editor': 'GIT_EDITOR',
357 \ 'core.askpass': 'GIT_ASKPASS',
359 function! fugitive#PrepareDirEnvArgv(...) abort
360 if a:0 && type(a:1) ==# type([])
361 let cmd = a:000[1:-1] + a:1
363 let cmd = copy(a:000)
368 if cmd[i] =~# '^$\|[\/.]' && cmd[i] !~# '^-'
369 let dir = remove(cmd, 0)
370 elseif cmd[i] =~# '^--git-dir='
371 let dir = remove(cmd, 0)[10:-1]
372 elseif type(cmd[i]) ==# type(0)
373 let dir = s:Dir(remove(cmd, i))
374 elseif cmd[i] ==# '-c' && len(cmd) > i + 1
375 let key = matchstr(cmd[i+1], '^[^=]*')
376 if has_key(s:prepare_env, tolower(key)) || key !~# '\.'
377 let var = get(s:prepare_env, tolower(key), key)
378 let val = matchstr(cmd[i+1], '=\zs.*')
381 if fugitive#GitVersion(1, 8) && cmd[i+1] =~# '\.'
384 call remove(cmd, i, i + 1)
386 elseif cmd[i] =~# '^--.*pathspecs$'
387 let explicit_pathspec_option = 1
388 if fugitive#GitVersion(1, 9)
393 elseif cmd[i] !~# '^-'
402 call s:PreparePathArgs(cmd, dir, !exists('explicit_pathspec_option'))
403 return [dir, env, cmd]
406 function! s:BuildShell(dir, env, args) abort
407 let cmd = copy(a:args)
408 let tree = s:Tree(a:dir)
410 for [var, val] in items(a:env)
412 let pre .= 'set ' . var . '=' . s:shellesc(val) . '& '
414 let pre = (len(pre) ? pre : 'env ') . var . '=' . s:shellesc(val) . ' '
417 if empty(tree) || index(cmd, '--') == len(cmd) - 1
418 call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
419 elseif fugitive#GitVersion(1, 8, 5)
420 call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep')
422 let pre = 'cd ' . s:shellesc(tree) . (s:winshell() ? '& ' : '; ') . pre
424 return pre . g:fugitive_git_executable . ' ' . join(map(cmd, 's:shellesc(v:val)'))
427 function! fugitive#Prepare(...) abort
428 let [dir, env, argv] = call('fugitive#PrepareDirEnvArgv', a:000)
429 return s:BuildShell(dir, env, argv)
432 function! s:SystemError(cmd, ...) abort
434 if &shellredir ==# '>' && &shell =~# 'sh\|cmd'
435 let shellredir = &shellredir
439 set shellredir=>%s\ 2>&1
442 let out = call('system', [type(a:cmd) ==# type([]) ? fugitive#Prepare(a:cmd) : a:cmd] + a:000)
443 return [out, v:shell_error]
444 catch /^Vim\%((\a\+)\)\=:E484:/
445 let opts = ['shell', 'shellcmdflag', 'shellredir', 'shellquote', 'shellxquote', 'shellxescape', 'shellslash']
446 call filter(opts, 'exists("+".v:val) && !empty(eval("&".v:val))')
447 call map(opts, 'v:val."=".eval("&".v:val)')
448 call s:throw('failed to run `' . a:cmd . '` with ' . join(opts, ' '))
450 if exists('shellredir')
451 let &shellredir = shellredir
456 function! s:ChompError(...) abort
457 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
458 return [s:sub(out, '\n$', ''), exec_error]
461 function! s:ChompDefault(default, ...) abort
462 let [out, exec_error] = call('s:ChompError', a:000)
463 return exec_error ? a:default : out
466 function! s:LinesError(...) abort
467 let [out, exec_error] = call('s:ChompError', a:000)
468 return [len(out) && !exec_error ? split(out, "\n", 1) : [], exec_error]
471 function! s:NullError(...) abort
472 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
473 return [exec_error ? [] : split(out, "\1"), exec_error ? substitute(out, "\n$", "", "") : '', exec_error]
476 function! s:TreeChomp(...) abort
477 let cmd = call('fugitive#Prepare', a:000)
478 let [out, exec_error] = s:SystemError(cmd)
479 let out = s:sub(out, '\n$', '')
483 throw 'fugitive: error running `' . cmd . '`: ' . out
486 function! s:EchoExec(...) abort
487 echo call('s:ChompError', a:000)[0]
488 call fugitive#ReloadStatus(-1, 1)
492 function! fugitive#Head(...) abort
493 let dir = a:0 > 1 ? a:2 : s:Dir()
494 if empty(dir) || !filereadable(fugitive#Find('.git/HEAD', dir))
497 let head = readfile(fugitive#Find('.git/HEAD', dir))[0]
499 return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
500 elseif head =~# '^\x\{40,\}$'
501 let len = a:0 ? a:1 : 0
502 return len < 0 ? head : len ? head[0:len-1] : ''
508 function! fugitive#RevParse(rev, ...) abort
509 let [hash, exec_error] = s:ChompError([a:0 ? a:1 : s:Dir(), 'rev-parse', '--verify', a:rev, '--'])
510 if !exec_error && hash =~# '^\x\{40,\}$'
513 throw 'fugitive: rev-parse '.a:rev.': '.hash
516 function! s:ConfigTimestamps(dir, dict) abort
517 let files = ['/etc/gitconfig', '~/.gitconfig',
518 \ len($XDG_CONFIG_HOME) ? $XDG_CONFIG_HOME . '/git/config' : '~/.config/git/config']
520 call add(files, fugitive#Find('.git/config', a:dir))
522 call extend(files, get(a:dict, 'include.path', []))
523 return join(map(files, 'getftime(expand(v:val))'), ',')
527 function! fugitive#Config(...) abort
530 if a:0 >= 2 && type(a:2) == type({})
531 let name = substitute(a:1, '^[^.]\+\|[^.]\+$', '\L&', 'g')
532 return len(a:1) ? get(get(a:2, name, []), 0, '') : a:2
536 elseif a:0 == 1 && type(a:1) == type({})
538 elseif a:0 == 1 && a:1 =~# '^[[:alnum:]-]\+\.'
543 let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
544 let key = len(dir) ? dir : '_'
545 if has_key(s:config, key) && s:config[key][0] ==# s:ConfigTimestamps(dir, s:config[key][1])
546 let dict = s:config[key][1]
549 let [lines, message, exec_error] = s:NullError([dir, 'config', '--list', '-z'])
554 let key = matchstr(line, "^[^\n]*")
555 if !has_key(dict, key)
558 call add(dict[key], strpart(line, len(key) + 1))
560 let s:config[dir] = [s:ConfigTimestamps(dir, dict), dict]
563 return len(name) ? get(get(dict, name, []), 0, '') : dict
566 function! s:Remote(dir) abort
567 let head = FugitiveHead(0, a:dir)
568 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
570 while remote ==# '.' && i > 0
571 let head = matchstr(fugitive#Config('branch.' . head . '.merge'), 'refs/heads/\zs.*')
572 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
575 return remote =~# '^\.\=$' ? 'origin' : remote
578 function! fugitive#RemoteUrl(...) abort
579 let dir = a:0 > 1 ? a:2 : s:Dir()
580 let remote = !a:0 || a:1 =~# '^\.\=$' ? s:Remote(dir) : a:1
581 if !fugitive#GitVersion(2, 7)
582 return fugitive#Config('remote.' . remote . '.url')
584 return s:ChompDefault('', [dir, 'remote', 'get-url', remote, '--'])
587 " Section: Repository Object
589 function! s:add_methods(namespace, method_names) abort
590 for name in a:method_names
591 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
595 let s:repo_prototype = {}
598 function! fugitive#repo(...) abort
599 let dir = a:0 ? s:Dir(a:1) : (len(s:Dir()) ? s:Dir() : FugitiveExtractGitDir(expand('%:p')))
601 if has_key(s:repos, dir)
602 let repo = get(s:repos, dir)
604 let repo = {'git_dir': dir}
605 let s:repos[dir] = repo
607 return extend(repo, s:repo_prototype, 'keep')
609 call s:throw('not a Git repository')
612 function! s:repo_dir(...) dict abort
613 return join([self.git_dir]+a:000,'/')
616 function! s:repo_tree(...) dict abort
617 let dir = s:Tree(self.git_dir)
619 call s:throw('no work tree')
621 return join([dir]+a:000,'/')
625 function! s:repo_bare() dict abort
626 if self.dir() =~# '/\.git$'
629 return s:Tree(self.git_dir) ==# ''
633 function! s:repo_find(object) dict abort
634 return fugitive#Find(a:object, self.git_dir)
637 function! s:repo_translate(rev) dict abort
638 return s:Slash(fugitive#Find(substitute(a:rev, '^/', ':(top)', ''), self.git_dir))
641 function! s:repo_head(...) dict abort
642 return fugitive#Head(a:0 ? a:1 : 0, self.git_dir)
645 call s:add_methods('repo',['dir','tree','bare','find','translate','head'])
647 function! s:repo_prepare(...) dict abort
648 return call('fugitive#Prepare', [self.git_dir] + a:000)
651 function! s:repo_git_command(...) dict abort
652 let git = s:UserCommand() . ' --git-dir='.s:shellesc(self.git_dir)
653 return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
656 function! s:repo_git_chomp(...) dict abort
657 let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
658 let output = git . join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
659 return s:sub(system(output), '\n$', '')
662 function! s:repo_git_chomp_in_tree(...) dict abort
663 let cdback = s:Cd(self.tree())
665 return call(self.git_chomp, a:000, self)
671 function! s:repo_rev_parse(rev) dict abort
672 return fugitive#RevParse(a:rev, self.git_dir)
675 call s:add_methods('repo',['prepare','git_command','git_chomp','git_chomp_in_tree','rev_parse'])
677 function! s:repo_superglob(base) dict abort
678 return map(fugitive#CompleteObject(a:base, self.git_dir), 'substitute(v:val, ''\\\(.\)'', ''\1'', "g")')
681 call s:add_methods('repo',['superglob'])
683 function! s:repo_config(name) dict abort
684 return fugitive#Config(a:name, self.git_dir)
687 function! s:repo_user() dict abort
688 let username = self.config('user.name')
689 let useremail = self.config('user.email')
690 return username.' <'.useremail.'>'
693 call s:add_methods('repo',['config', 'user'])
697 function! s:DirCommitFile(path) abort
698 let vals = matchlist(s:Slash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40,\}\|[0-3]\)\(/.*\)\=$')
705 function! s:DirRev(url) abort
706 let [dir, commit, file] = s:DirCommitFile(a:url)
707 return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
710 let s:merge_heads = ['MERGE_HEAD', 'REBASE_HEAD', 'CHERRY_PICK_HEAD', 'REVERT_HEAD']
711 function! s:MergeHead(...) abort
712 let dir = fugitive#Find('.git/', a:0 ? a:1 : s:Dir())
713 for head in s:merge_heads
714 if filereadable(dir . head)
721 function! s:Owner(path, ...) abort
722 let dir = a:0 ? a:1 : s:Dir()
726 let actualdir = fugitive#Find('.git/', dir)
727 let [pdir, commit, file] = s:DirCommitFile(a:path)
728 if s:cpath(dir, pdir)
729 if commit =~# '^\x\{40,\}$'
731 elseif commit ==# '2'
733 elseif commit ==# '0'
736 let merge_head = s:MergeHead()
741 return merge_head . '^{}'
742 elseif commit ==# '1'
743 return s:TreeChomp('merge-base', 'HEAD', merge_head, '--')
746 let path = fnamemodify(a:path, ':p')
747 if s:cpath(actualdir, strpart(path, 0, len(actualdir))) && a:path =~# 'HEAD$'
748 return strpart(path, len(actualdir))
750 let refs = fugitive#Find('.git/refs', dir)
751 if s:cpath(refs . '/', path[0 : len(refs)]) && path !~# '[\/]$'
752 return strpart(path, len(refs) - 4)
757 function! fugitive#Real(url) abort
761 let [dir, commit, file] = s:DirCommitFile(a:url)
763 let tree = s:Tree(dir)
764 return FugitiveVimPath((len(tree) ? tree : dir) . file)
766 let pre = substitute(matchstr(a:url, '^\a\a\+\ze:'), '^.', '\u&', '')
767 if len(pre) && pre !=? 'fugitive' && exists('*' . pre . 'Real')
768 let url = {pre}Real(a:url)
770 let url = fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??'))
772 return FugitiveVimPath(empty(url) ? a:url : url)
775 function! fugitive#Path(url, ...) abort
779 let dir = a:0 > 1 ? a:2 : s:Dir()
780 let tree = s:Tree(dir)
782 return fugitive#Real(a:url)
784 let path = s:Slash(fugitive#Real(a:url))
787 while s:cpath(tree . '/', (cwd . '/')[0 : len(tree)])
788 if s:cpath(cwd . '/', path[0 : len(cwd)])
789 if strpart(path, len(cwd) + 1) =~# '^\.git\%(/\|$\)'
792 return a:1[0:-2] . (empty(lead) ? './' : lead) . strpart(path, len(cwd) + 1)
794 let cwd = fnamemodify(cwd, ':h')
797 return a:1[0:-2] . path
800 let temp_state = s:TempState(url)
801 if has_key(temp_state, 'bufnr')
802 let url = bufname(temp_state.bufnr)
804 let url = s:Slash(fnamemodify(url, ':p'))
805 if url =~# '/$' && s:Slash(a:url) !~# '/$'
808 let [argdir, commit, file] = s:DirCommitFile(a:url)
809 if len(argdir) && s:cpath(argdir) !=# s:cpath(dir)
811 elseif len(dir) && s:cpath(url[0 : len(dir)]) ==# s:cpath(dir . '/')
812 let file = '/.git'.url[strlen(dir) : -1]
813 elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/')
814 let file = url[len(tree) : -1]
815 elseif s:cpath(url) ==# s:cpath(tree)
818 if empty(file) && a:1 =~# '^$\|^[.:]/$'
819 return FugitiveGitPath(fugitive#Real(a:url))
821 return substitute(file, '^/', a:1, '')
824 function! s:Relative(...) abort
825 return fugitive#Path(@%, a:0 ? a:1 : ':(top)', a:0 > 1 ? a:2 : s:Dir())
828 function! fugitive#Find(object, ...) abort
829 if type(a:object) == type(0)
830 let name = bufname(a:object)
831 return FugitiveVimPath(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name)
832 elseif a:object =~# '^[~$]'
833 let prefix = matchstr(a:object, '^[~$]\i*')
834 let owner = expand(prefix)
835 return FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix)))
836 elseif s:Slash(a:object) =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
837 return FugitiveVimPath(a:object)
838 elseif s:Slash(a:object) =~# '^\.\.\=\%(/\|$\)'
839 return FugitiveVimPath(simplify(getcwd() . '/' . a:object))
841 let dir = a:0 ? a:1 : s:Dir()
843 let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs.*', '', '')
844 let dir = FugitiveExtractGitDir(file)
846 return fnamemodify(FugitiveVimPath(len(file) ? file : a:object), ':p')
849 let rev = s:Slash(a:object)
850 let tree = s:Tree(dir)
851 let base = len(tree) ? tree : 'fugitive://' . dir . '//0'
853 let f = len(tree) ? tree . '/.git' : dir
854 elseif rev =~# '^\.git/'
855 let f = substitute(rev, '^\.git', '', '')
856 let cdir = fugitive#CommonDir(dir)
857 if f =~# '^/\.\./\.\.\%(/\|$\)'
858 let f = simplify(len(tree) ? tree . f[3:-1] : dir . f)
859 elseif f =~# '^/\.\.\%(/\|$\)'
860 let f = base . f[3:-1]
861 elseif cdir !=# dir && (
862 \ f =~# '^/\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' ||
863 \ f !~# '^/\%(index$\|index\.lock$\|\w*MSG$\|\w*HEAD$\|logs/\w*HEAD$\|logs$\|rebase-\w\+\)\%(/\|$\)' &&
864 \ getftime(FugitiveVimPath(dir . f)) < 0 && getftime(FugitiveVimPath(cdir . f)) >= 0)
865 let f = simplify(cdir . f)
867 let f = simplify(dir . f)
871 elseif rev =~# '^\.\%(/\|$\)'
872 let f = base . rev[1:-1]
873 elseif rev =~# '^::\%(/\|\a\+\:\)'
875 elseif rev =~# '^::\.\.\=\%(/\|$\)'
876 let f = simplify(getcwd() . '/' . rev[2:-1])
878 let f = base . '/' . rev[2:-1]
879 elseif rev =~# '^:\%([0-3]:\)\=\.\.\=\%(/\|$\)\|^:[0-3]:\%(/\|\a\+:\)'
880 let f = rev =~# '^:\%([0-3]:\)\=\.' ? simplify(getcwd() . '/' . matchstr(rev, '\..*')) : rev[3:-1]
881 if s:cpath(base . '/', (f . '/')[0 : len(base)])
882 let f = 'fugitive://' . dir . '//' . +matchstr(rev, '^:\zs\d\ze:') . '/' . strpart(f, len(base) + 1)
884 let altdir = FugitiveExtractGitDir(f)
885 if len(altdir) && !s:cpath(dir, altdir)
886 return fugitive#Find(a:object, altdir)
889 elseif rev =~# '^:[0-3]:'
890 let f = 'fugitive://' . dir . '//' . rev[1] . '/' . rev[3:-1]
892 if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && s:cpath(fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(dir)]) ==# s:cpath(dir . '/') && filereadable($GIT_INDEX_FILE)
893 let f = fnamemodify($GIT_INDEX_FILE, ':p')
895 let f = fugitive#Find('.git/index', dir)
897 elseif rev =~# '^:(\%(top\|top,literal\|literal,top\|literal\))'
898 let f = matchstr(rev, ')\zs.*')
899 if f=~# '^\.\.\=\%(/\|$\)'
900 let f = simplify(getcwd() . '/' . f)
901 elseif f !~# '^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
902 let f = base . '/' . f
904 elseif rev =~# '^:/\@!'
905 let f = 'fugitive://' . dir . '//0/' . rev[1:-1]
908 let commit = substitute(matchstr(rev, '^[^:.-][^:]*\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '')
909 let file = substitute(matchstr(rev, '^[^:.-][^:]*\zs:.*'), '^:', '/', '')
910 if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:'
911 let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1]
912 if s:cpath(base . '/', (file . '/')[0 : len(base)])
913 let file = '/' . strpart(file, len(base) + 1)
915 let altdir = FugitiveExtractGitDir(file)
916 if len(altdir) && !s:cpath(dir, altdir)
917 return fugitive#Find(a:object, altdir)
922 let commits = split(commit, '\.\.\.-\@!', 1)
924 call map(commits, 'empty(v:val) || v:val ==# "@" ? "HEAD" : v:val')
925 let commit = matchstr(s:ChompDefault('', [dir, 'merge-base'] + commits + ['--']), '\<[0-9a-f]\{40,\}\>')
927 if commit !~# '^[0-9a-f]\{40,\}$'
928 let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit, '--']), '\<[0-9a-f]\{40,\}\>')
931 let f = 'fugitive://' . dir . '//' . commit . file
933 let f = base . '/' . substitute(rev, '^:/:\=\|^[^:]\+:', '', '')
937 return FugitiveVimPath(f)
940 function! s:Generate(rev, ...) abort
941 return fugitive#Find(a:rev, a:0 ? a:1 : s:Dir())
944 function! s:DotRelative(path, ...) abort
945 let cwd = a:0 ? a:1 : getcwd()
946 let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
947 if len(cwd) && s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
948 return '.' . strpart(path, len(cwd))
953 function! fugitive#Object(...) abort
954 let dir = a:0 > 1 ? a:2 : s:Dir()
955 let [fdir, rev] = s:DirRev(a:0 ? a:1 : @%)
956 if s:cpath(dir) !=# s:cpath(fdir)
959 let tree = s:Tree(dir)
960 let full = a:0 ? a:1 : @%
961 let full = fnamemodify(full, ':p' . (s:Slash(full) =~# '/$' ? '' : ':s?/$??'))
962 if empty(rev) && empty(tree)
963 return FugitiveGitPath(full)
965 let rev = fugitive#Path(full, './', dir)
966 if rev =~# '^\./.git\%(/\|$\)'
967 return FugitiveGitPath(full)
970 if rev !~# '^\.\%(/\|$\)' || s:cpath(getcwd(), tree)
973 return FugitiveGitPath(tree . rev[1:-1])
977 let s:var = '\%(%\|#<\=\d\+\|##\=\)'
978 let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
979 let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
981 function! s:BufName(var) abort
983 return bufname(get(s:TempState(), 'bufnr', ''))
984 elseif a:var =~# '^#\d*$'
985 let nr = get(s:TempState(bufname(+a:var[1:-1])), 'bufnr', '')
986 return bufname(nr ? nr : +a:var[1:-1])
992 function! s:ExpandVarLegacy(str) abort
993 if get(g:, 'fugitive_legacy_quoting', 1)
994 return substitute(a:str, '\\\ze[%#!]', '', 'g')
1000 function! s:ExpandVar(other, var, flags, esc, ...) abort
1001 let cwd = a:0 ? a:1 : getcwd()
1003 return a:other[1:-1]
1004 elseif a:other =~# '^'''
1005 return s:ExpandVarLegacy(substitute(a:other[1:-2], "''", "'", "g"))
1006 elseif a:other =~# '^"'
1007 return s:ExpandVarLegacy(substitute(a:other[1:-2], '""', '"', "g"))
1008 elseif a:other =~# '^!'
1009 let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
1010 let owner = s:Owner(buffer)
1011 return len(owner) ? owner : '@'
1014 let file = s:DotRelative(fugitive#Real(s:BufName(a:var)), cwd)
1016 let flag = matchstr(flags, s:flag)
1017 let flags = strpart(flags, len(flag))
1019 let file = s:DotRelative(file, cwd)
1021 let file = fnamemodify(file, flag)
1024 let file = s:Slash(file)
1025 return (len(a:esc) ? shellescape(file) : file)
1028 function! s:Expand(rev, ...) abort
1029 if a:rev =~# '^:[0-3]$'
1030 let file = len(expand('%')) ? a:rev . ':%' : '%'
1031 elseif a:rev ==# '>'
1033 elseif a:rev =~# '^>[~^]'
1034 let file = len(expand('%')) ? '!' . a:rev[1:-1] . ':%' : '%'
1035 elseif a:rev =~# '^>[> ]\@!'
1036 let file = len(expand('%')) ? a:rev[1:-1] . ':%' : '%'
1040 return substitute(file,
1041 \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1042 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),"", a:0 ? a:1 : getcwd())', 'g')
1045 function! fugitive#Expand(object) abort
1046 return substitute(a:object,
1047 \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1048 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
1051 function! s:ExpandSplit(string, ...) abort
1053 let string = a:string
1054 let handle_bar = a:0 && a:1
1055 let dquote = handle_bar ? '"\%([^"]\|""\|\\"\)*"\|' : ''
1056 let cwd = a:0 > 1 ? a:2 : getcwd()
1057 while string =~# '\S'
1058 if handle_bar && string =~# '^\s*|'
1059 return [list, substitute(string, '^\s*', '', '')]
1061 let arg = matchstr(string, '^\s*\%(' . dquote . '''[^'']*''\|\\.\|[^[:space:] ' . (handle_bar ? '|' : '') . ']\)\+')
1062 let string = strpart(string, len(arg))
1063 let arg = substitute(arg, '^\s\+', '', '')
1064 if !exists('seen_separator')
1065 let arg = substitute(arg, '^\%([^:.][^:]*:\|^:\|^:[0-3]:\)\=\zs\.\.\=\%(/.*\)\=$',
1066 \ '\=s:DotRelative(s:Slash(simplify(getcwd() . "/" . submatch(0))), cwd)', '')
1068 let arg = substitute(arg,
1069 \ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1070 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5), cwd)', 'g')
1073 let seen_separator = 1
1076 return handle_bar ? [list, ''] : list
1079 function! s:SplitExpand(string, ...) abort
1080 return s:ExpandSplit(a:string, 0, a:0 ? a:1 : getcwd())
1083 function! s:SplitExpandChain(string, ...) abort
1084 return s:ExpandSplit(a:string, 1, a:0 ? a:1 : getcwd())
1089 function! s:TreeInfo(dir, commit) abort
1090 if a:commit =~# '^:\=[0-3]$'
1091 let index = get(s:indexes, a:dir, [])
1092 let newftime = getftime(fugitive#Find('.git/index', a:dir))
1093 if get(index, 0, -1) < newftime
1094 let [lines, exec_error] = s:LinesError([a:dir, 'ls-files', '--stage', '--'])
1095 let s:indexes[a:dir] = [newftime, {'0': {}, '1': {}, '2': {}, '3': {}}]
1100 let [info, filename] = split(line, "\t")
1101 let [mode, sha, stage] = split(info, '\s\+')
1102 let s:indexes[a:dir][1][stage][filename] = [newftime, mode, 'blob', sha, -2]
1103 while filename =~# '/'
1104 let filename = substitute(filename, '/[^/]*$', '', '')
1105 let s:indexes[a:dir][1][stage][filename] = [newftime, '040000', 'tree', '', 0]
1109 return [get(s:indexes[a:dir][1], a:commit[-1:-1], {}), newftime]
1110 elseif a:commit =~# '^\x\{40,\}$'
1111 if !has_key(s:trees, a:dir)
1112 let s:trees[a:dir] = {}
1114 if !has_key(s:trees[a:dir], a:commit)
1115 let [ftime, exec_error] = s:ChompError([a:dir, 'log', '-1', '--pretty=format:%ct', a:commit, '--'])
1117 let s:trees[a:dir][a:commit] = [{}, -1]
1118 return s:trees[a:dir][a:commit]
1120 let s:trees[a:dir][a:commit] = [{}, +ftime]
1121 let [lines, exec_error] = s:LinesError([a:dir, 'ls-tree', '-rtl', '--full-name', a:commit, '--'])
1123 return s:trees[a:dir][a:commit]
1126 let [info, filename] = split(line, "\t")
1127 let [mode, type, sha, size] = split(info, '\s\+')
1128 let s:trees[a:dir][a:commit][0][filename] = [+ftime, mode, type, sha, +size, filename]
1131 return s:trees[a:dir][a:commit]
1136 function! s:PathInfo(url) abort
1137 let [dir, commit, file] = s:DirCommitFile(a:url)
1138 if empty(dir) || !get(g:, 'fugitive_file_api', 1)
1139 return [-1, '000000', '', '', -1]
1141 let path = substitute(file[1:-1], '/*$', '', '')
1142 let [tree, ftime] = s:TreeInfo(dir, commit)
1143 let entry = empty(path) ? [ftime, '040000', 'tree', '', -1] : get(tree, path, [])
1144 if empty(entry) || file =~# '/$' && entry[2] !=# 'tree'
1145 return [-1, '000000', '', '', -1]
1151 function! fugitive#simplify(url) abort
1152 let [dir, commit, file] = s:DirCommitFile(a:url)
1156 if file =~# '/\.\.\%(/\|$\)'
1157 let tree = s:Tree(dir)
1159 let path = simplify(tree . file)
1160 if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/'
1161 return FugitiveVimPath(path)
1165 return FugitiveVimPath('fugitive://' . simplify(dir) . '//' . commit . simplify(file))
1168 function! fugitive#resolve(url) abort
1169 let url = fugitive#simplify(a:url)
1170 if url =~? '^fugitive:'
1177 function! fugitive#getftime(url) abort
1178 return s:PathInfo(a:url)[0]
1181 function! fugitive#getfsize(url) abort
1182 let entry = s:PathInfo(a:url)
1183 if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3])
1184 let dir = s:DirCommitFile(a:url)[0]
1185 let entry[4] = +s:ChompDefault(-1, [dir, 'cat-file', '-s', entry[3]])
1190 function! fugitive#getftype(url) abort
1191 return get({'tree': 'dir', 'blob': 'file'}, s:PathInfo(a:url)[2], '')
1194 function! fugitive#filereadable(url) abort
1195 return s:PathInfo(a:url)[2] ==# 'blob'
1198 function! fugitive#filewritable(url) abort
1199 let [dir, commit, file] = s:DirCommitFile(a:url)
1200 if commit !~# '^\d$' || !filewritable(fugitive#Find('.git/index', dir))
1203 return s:PathInfo(a:url)[2] ==# 'blob' ? 1 : 2
1206 function! fugitive#isdirectory(url) abort
1207 return s:PathInfo(a:url)[2] ==# 'tree'
1210 function! fugitive#getfperm(url) abort
1211 let [dir, commit, file] = s:DirCommitFile(a:url)
1212 let perm = getfperm(dir)
1213 let fperm = s:PathInfo(a:url)[1]
1214 if fperm ==# '040000'
1215 let fperm = '000755'
1218 let perm = tr(perm, 'x', '-')
1220 if fperm !~# '[45]$'
1221 let perm = tr(perm, 'rw', '--')
1223 if commit !~# '^\d$'
1224 let perm = tr(perm, 'w', '-')
1226 return perm ==# '---------' ? '' : perm
1229 function! fugitive#setfperm(url, perm) abort
1230 let [dir, commit, file] = s:DirCommitFile(a:url)
1231 let entry = s:PathInfo(a:url)
1232 let perm = fugitive#getfperm(a:url)
1233 if commit !~# '^\d$' || entry[2] !=# 'blob' ||
1234 \ substitute(perm, 'x', '-', 'g') !=# substitute(a:perm, 'x', '-', 'g')
1237 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1238 \ (a:perm =~# 'x' ? '000755 ' : '000644 ') . entry[3] . ' ' . commit . "\t" . file[1:-1])[1]
1239 return exec_error ? -1 : 0
1242 function! s:TempCmd(out, cmd) abort
1245 let cmd = (type(a:cmd) == type([]) ? fugitive#Prepare(a:cmd) : a:cmd)
1246 let redir = ' > ' . a:out
1248 let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
1249 return s:SystemError('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"')
1250 elseif &shell =~# 'fish'
1251 return s:SystemError(' begin;' . prefix . cmd . redir . ';end ')
1253 return s:SystemError(' (' . prefix . cmd . redir . ') ')
1258 if !exists('s:blobdirs')
1261 function! s:BlobTemp(url) abort
1262 let [dir, commit, file] = s:DirCommitFile(a:url)
1266 if !has_key(s:blobdirs, dir)
1267 let s:blobdirs[dir] = tempname()
1269 let tempfile = s:blobdirs[dir] . '/' . commit . file
1270 let tempparent = fnamemodify(tempfile, ':h')
1271 if !isdirectory(tempparent)
1272 call mkdir(tempparent, 'p')
1274 if commit =~# '^\d$' || !filereadable(tempfile)
1275 let rev = s:DirRev(a:url)[1]
1276 let exec_error = s:TempCmd(tempfile, [dir, 'cat-file', 'blob', rev])[1]
1278 call delete(tempfile)
1282 return s:Resolve(tempfile)
1285 function! fugitive#readfile(url, ...) abort
1286 let entry = s:PathInfo(a:url)
1287 if entry[2] !=# 'blob'
1290 let temp = s:BlobTemp(a:url)
1294 return call('readfile', [temp] + a:000)
1297 function! fugitive#writefile(lines, url, ...) abort
1298 let url = type(a:url) ==# type('') ? a:url : ''
1299 let [dir, commit, file] = s:DirCommitFile(url)
1300 let entry = s:PathInfo(url)
1301 if commit =~# '^\d$' && entry[2] !=# 'tree'
1302 let temp = tempname()
1303 if a:0 && a:1 =~# 'a' && entry[2] ==# 'blob'
1304 call writefile(fugitive#readfile(url, 'b'), temp, 'b')
1306 call call('writefile', [a:lines, temp] + a:000)
1307 let [hash, exec_error] = s:ChompError([dir, 'hash-object', '-w', temp])
1308 let mode = len(entry[1]) ? entry[1] : '100644'
1309 if !exec_error && hash =~# '^\x\{40,\}$'
1310 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1311 \ mode . ' ' . hash . ' ' . commit . "\t" . file[1:-1])[1]
1317 return call('writefile', [a:lines, a:url] + a:000)
1321 \ '/**/': '/\%([^./][^/]*/\)*',
1322 \ '/**': '/\%([^./][^/]\+/\)*[^./][^/]*',
1323 \ '**/': '[^/]*\%(/[^./][^/]*\)*',
1325 \ '/*': '/[^/.][^/]*',
1328 function! fugitive#glob(url, ...) abort
1329 let [dirglob, commit, glob] = s:DirCommitFile(a:url)
1330 let append = matchstr(glob, '/*$')
1331 let glob = substitute(glob, '/*$', '', '')
1332 let pattern = '^' . substitute(glob, '/\=\*\*/\=\|/\=\*\|[.?\$]\|^^', '\=get(s:globsubs, submatch(0), "\\" . submatch(0))', 'g')[1:-1] . '$'
1334 for dir in dirglob =~# '[*?]' ? split(glob(dirglob), "\n") : [dirglob]
1335 if empty(dir) || !get(g:, 'fugitive_file_api', 1) || !filereadable(fugitive#Find('.git/HEAD', dir))
1338 let files = items(s:TreeInfo(dir, commit)[0])
1340 call filter(files, 'v:val[1][2] ==# "tree"')
1342 call map(files, 'v:val[0]')
1343 call filter(files, 'v:val =~# pattern')
1344 let prepend = 'fugitive://' . dir . '//' . substitute(commit, '^:', '', '') . '/'
1346 call map(files, 'FugitiveVimPath(prepend . v:val . append)')
1347 call extend(results, files)
1352 return join(results, "\n")
1356 function! fugitive#delete(url, ...) abort
1357 let [dir, commit, file] = s:DirCommitFile(a:url)
1358 if a:0 && len(a:1) || commit !~# '^\d$'
1361 let entry = s:PathInfo(a:url)
1362 if entry[2] !=# 'blob'
1365 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1366 \ '000000 0000000000000000000000000000000000000000 ' . commit . "\t" . file[1:-1])[1]
1367 return exec_error ? -1 : 0
1370 " Section: Buffer Object
1372 let s:buffer_prototype = {}
1374 function! fugitive#buffer(...) abort
1375 let buffer = {'#': bufnr(a:0 ? a:1 : '%')}
1376 call extend(buffer, s:buffer_prototype, 'keep')
1380 function! s:buffer_repo() dict abort
1381 return fugitive#repo(self['#'])
1384 function! s:buffer_type(...) dict abort
1385 return 'see b:fugitive_type'
1388 call s:add_methods('buffer', ['repo', 'type'])
1390 " Section: Completion
1392 function! s:FilterEscape(items, ...) abort
1393 let items = copy(a:items)
1394 if a:0 && type(a:1) == type('')
1395 call filter(items, 'strpart(v:val, 0, strlen(a:1)) ==# a:1')
1397 return map(items, 's:fnameescape(v:val)')
1400 function! s:GlobComplete(lead, pattern) abort
1403 elseif v:version >= 704
1404 let results = glob(a:lead . a:pattern, 0, 1)
1406 let results = split(glob(a:lead . a:pattern), "\n")
1408 call map(results, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val')
1409 call map(results, 'v:val[ strlen(a:lead) : -1 ]')
1413 function! fugitive#CompletePath(base, ...) abort
1414 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1415 let tree = s:Tree(dir) . '/'
1416 let strip = '^\%(:/:\=\|:(top)\|:(top,literal)\|:(literal,top)\|:(literal)\)'
1417 let base = substitute(a:base, strip, '', '')
1418 if base =~# '^\.git/'
1419 let pattern = s:gsub(base[5:-1], '/', '*&').'*'
1420 let matches = s:GlobComplete(dir . '/', pattern)
1421 let cdir = fugitive#CommonDir(dir)
1422 if len(cdir) && s:cpath(dir) !=# s:cpath(cdir)
1423 call extend(matches, s:GlobComplete(cdir . '/', pattern))
1425 call s:Uniq(matches)
1426 call map(matches, "'.git/' . v:val")
1427 elseif base =~# '^\~/'
1428 let matches = map(s:GlobComplete(expand('~/'), base[2:-1] . '*'), '"~/" . v:val')
1429 elseif a:base =~# '^/\|^\a\+:\|^\.\.\=/\|^:(literal)'
1430 let matches = s:GlobComplete('', base . '*')
1431 elseif len(tree) > 1
1432 let matches = s:GlobComplete(tree, s:gsub(base, '/', '*&').'*')
1436 call map(matches, 's:fnameescape(s:Slash(matchstr(a:base, strip) . v:val))')
1440 function! fugitive#PathComplete(...) abort
1441 return call('fugitive#CompletePath', a:000)
1444 function! s:CompleteHeads(dir) abort
1445 let dir = fugitive#Find('.git/', a:dir)
1446 return sort(filter(['HEAD', 'FETCH_HEAD', 'ORIG_HEAD'] + s:merge_heads, 'filereadable(dir . v:val)')) +
1447 \ sort(s:LinesError('rev-parse', '--symbolic', '--branches', '--tags', '--remotes')[0])
1450 function! fugitive#CompleteObject(base, ...) abort
1451 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1453 let tree = s:Tree(dir) . '/'
1455 if len(tree) > 1 && s:cpath(tree, cwd[0 : len(tree) - 1])
1456 let subdir = strpart(cwd, len(tree)) . '/'
1459 if a:base =~# '^\.\=/\|^:(' || a:base !~# ':'
1461 if a:base =~# '^refs/'
1462 let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)')
1463 elseif a:base !~# '^\.\=/\|^:('
1464 let heads = s:CompleteHeads(dir)
1465 if filereadable(fugitive#Find('.git/refs/stash', dir))
1466 let heads += ["stash"]
1467 let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0])
1469 call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
1470 let results += heads
1472 call map(results, 's:fnameescape(v:val)')
1474 let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base)
1478 elseif a:base =~# '^:'
1479 let entries = s:LinesError(['ls-files','--stage'], dir)[0]
1480 if a:base =~# ':\./'
1481 call map(entries, 'substitute(v:val, "\\M\t\\zs" . subdir, "./", "")')
1483 call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
1484 if a:base !~# '^:[0-3]\%(:\|$\)'
1485 call filter(entries,'v:val[1] == "0"')
1486 call map(entries,'v:val[2:-1]')
1490 let tree = matchstr(a:base, '.*[:/]')
1491 let entries = s:LinesError(['ls-tree', substitute(tree, ':\zs\./', '\=subdir', '')], dir)[0]
1492 call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
1493 call map(entries,'tree.s:sub(v:val,".*\t","")')
1496 return s:FilterEscape(entries, a:base)
1499 function! s:CompleteSub(subcommand, A, L, P, ...) abort
1500 let pre = strpart(a:L, 0, a:P)
1502 return fugitive#CompletePath(a:A)
1503 elseif a:A =~# '^-' || a:A is# 0
1504 return s:FilterEscape(split(s:ChompDefault('', a:subcommand, '--git-completion-helper'), ' '), a:A)
1506 return fugitive#CompleteObject(a:A, s:Dir())
1507 elseif type(a:1) == type(function('tr'))
1508 return call(a:1, [a:A, a:L, a:P])
1510 return s:FilterEscape(a:1, a:A)
1514 function! s:CompleteRevision(A, L, P, ...) abort
1515 return s:FilterEscape(s:CompleteHeads(s:Dir()), a:A)
1518 function! s:CompleteRemote(A, L, P) abort
1519 let remote = matchstr(a:L, '\u\w*[! ] *\zs\S\+\ze ')
1521 let matches = s:LinesError('ls-remote', remote)[0]
1522 call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
1523 call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
1525 let matches = s:LinesError('remote')[0]
1527 return s:FilterEscape(matches, a:A)
1530 " Section: Buffer auto-commands
1532 function! s:ReplaceCmd(cmd) abort
1533 let temp = tempname()
1534 let [err, exec_error] = s:TempCmd(temp, a:cmd)
1536 call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
1538 let temp = s:Resolve(temp)
1539 let fn = expand('%:p')
1540 silent exe 'keepalt file '.temp
1541 let modelines = &modelines
1544 silent keepjumps noautocmd edit!
1546 let &modelines = modelines
1548 silent exe 'keepalt file '.s:fnameescape(fn)
1549 catch /^Vim\%((\a\+)\)\=:E302:/
1552 if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
1553 silent execute 'bwipeout '.bufnr('$')
1558 function! s:QueryLog(refspec) abort
1559 let lines = s:LinesError(['log', '-n', '256', '--format=%h%x09%s', a:refspec, '--'])[0]
1560 call map(lines, 'split(v:val, "\t")')
1561 call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
1565 function! s:FormatLog(dict) abort
1566 return a:dict.commit . ' ' . a:dict.subject
1569 function! s:FormatRebase(dict) abort
1570 return a:dict.status . ' ' . a:dict.commit . ' ' . a:dict.subject
1573 function! s:FormatFile(dict) abort
1574 return a:dict.status . ' ' . a:dict.filename
1577 function! s:Format(val) abort
1578 if type(a:val) == type({})
1579 return s:Format{a:val.type}(a:val)
1580 elseif type(a:val) == type([])
1581 return map(copy(a:val), 's:Format(v:val)')
1587 function! s:AddHeader(key, value) abort
1592 while !empty(getline(before))
1595 call append(before - 1, [a:key . ':' . (len(a:value) ? ' ' . a:value : '')])
1596 if before == 1 && line('$') == 2
1597 silent keepjumps 2delete _
1601 function! s:AddSection(label, lines, ...) abort
1602 let note = a:0 ? a:1 : ''
1603 if empty(a:lines) && empty(note)
1606 call append(line('$'), ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines))
1609 function! fugitive#BufReadStatus() abort
1610 let amatch = s:Slash(expand('%:p'))
1611 let b:fugitive_type = 'index'
1612 unlet! b:fugitive_reltime
1614 silent doautocmd BufReadPre
1615 let cmd = [fnamemodify(amatch, ':h')]
1616 setlocal noro ma nomodeline buftype=nowrite
1617 if s:cpath(fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : fugitive#Find('.git/index'), ':p')) !=# s:cpath(amatch)
1618 let cmd += ['-c', 'GIT_INDEX_FILE=' . amatch]
1621 let b:fugitive_files = {'Staged': {}, 'Unstaged': {}}
1622 let [staged, unstaged, untracked] = [[], [], []]
1624 if fugitive#GitVersion(2, 11)
1625 let cmd += ['status', '--porcelain=v2', '-bz']
1626 let [output, message, exec_error] = s:NullError(cmd)
1628 throw 'fugitive: ' . message
1631 let i = match(output, '^[^#]')
1632 let head = matchlist(output[:i], '^# branch\.head \zs.*$')[0]
1633 let pull = get(matchlist(output[:i], '^# branch\.upstream \zs.*$'), 0, '')
1636 elseif head ==# '(detached)'
1637 let head = matchlist(output[:i], '^# branch\.oid \zs.*$')[0][:10]
1643 while i < len(output)
1644 let line = output[i]
1646 call add(untracked, {'type': 'File', 'status': line[0], 'filename': line[2:-1]})
1647 elseif line[0] !=# '#'
1649 let file = matchstr(line, '^.\{37\} \x\{40,\} \x\{40,\} \x\{40,\} \zs.*$')
1651 let file = matchstr(line, '^.\{30\} \x\{40,\} \x\{40,\} \zs.*$')
1655 let file = output[i] . ' -> ' . matchstr(file, ' \zs.*')
1657 let sub = matchstr(line, '^[12u] .. \zs....')
1659 call add(staged, {'type': 'File', 'status': line[2], 'filename': file, 'sub': sub})
1662 call add(unstaged, {'type': 'File', 'status': get({'C':'M','M':'?','U':'?'}, matchstr(sub, 'S\.*\zs[CMU]'), line[3]), 'filename': file, 'sub': sub})
1668 let cmd += ['status', '--porcelain', '-bz']
1669 let [output, message, exec_error] = s:NullError(cmd)
1671 throw 'fugitive: ' . message
1674 let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
1676 if head =~# '\.\.\.'
1677 let [head, pull] = split(head, '\.\.\.')
1679 elseif head ==# 'HEAD' || empty(head)
1680 let head = FugitiveHead(11)
1687 while i < len(output)
1688 let line = output[i]
1689 let file = line[3:-1]
1695 if line[0:1] =~# '[RC]'
1696 let files = output[i] . ' -> ' . file
1699 if line[0] !~# '[ ?!#]'
1700 call add(staged, {'type': 'File', 'status': line[0], 'filename': files, 'sub': ''})
1702 if line[0:1] ==# '??'
1703 call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
1704 elseif line[1] !~# '[ !#]'
1705 call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files, 'sub': ''})
1711 let b:fugitive_files['Staged'][dict.filename] = dict
1713 for dict in unstaged
1714 let b:fugitive_files['Unstaged'][dict.filename] = dict
1717 let config = fugitive#Config()
1719 let pull_type = 'Pull'
1721 let rebase = fugitive#Config('branch.' . branch . '.rebase', config)
1723 let rebase = fugitive#Config('pull.rebase', config)
1725 if rebase =~# '^\%(true\|yes\|on\|1\|interactive\)$'
1726 let pull_type = 'Rebase'
1727 elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
1728 let pull_type = 'Merge'
1732 let push_remote = fugitive#Config('branch.' . branch . '.pushRemote', config)
1733 if empty(push_remote)
1734 let push_remote = fugitive#Config('remote.pushDefault', config)
1736 let push = len(push_remote) && len(branch) ? push_remote . '/' . branch : ''
1742 let unpulled = s:QueryLog(head . '..' . pull)
1747 let unpushed = s:QueryLog(push . '..' . head)
1752 if isdirectory(fugitive#Find('.git/rebase-merge/'))
1753 let rebasing_dir = fugitive#Find('.git/rebase-merge/')
1754 elseif isdirectory(fugitive#Find('.git/rebase-apply/'))
1755 let rebasing_dir = fugitive#Find('.git/rebase-apply/')
1759 let rebasing_head = 'detached HEAD'
1760 if exists('rebasing_dir') && filereadable(rebasing_dir . 'git-rebase-todo')
1761 let rebasing_head = substitute(readfile(rebasing_dir . 'head-name')[0], '\C^refs/heads/', '', '')
1763 let lines = readfile(rebasing_dir . 'git-rebase-todo')
1765 let hash = matchstr(line, '^[^a-z].*\s\zs[0-9a-f]\{4,\}\ze\.\.')
1771 if getfsize(rebasing_dir . 'done') > 0
1772 let done = readfile(rebasing_dir . 'done')
1773 call map(done, 'substitute(v:val, ''^\l\+\>'', "done", "")')
1774 let done[-1] = substitute(done[-1], '^\l\+\>', 'stop', '')
1775 let lines = done + lines
1779 let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
1780 if len(match) && match[1] !~# 'exec\|merge\|label'
1781 call add(rebasing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': strpart(match[2], 0, len), 'subject': match[3]})
1786 let diff = {'Staged': [], 'Unstaged': []}
1788 let diff['Staged'] =
1789 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached'])[0]
1792 let diff['Unstaged'] =
1793 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix'])[0]
1795 let b:fugitive_diff = diff
1796 let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}})
1797 let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}}
1799 silent keepjumps %delete_
1801 call s:AddHeader('Head', head)
1802 call s:AddHeader(pull_type, pull)
1804 call s:AddHeader('Push', push)
1806 call s:AddSection('Rebasing ' . rebasing_head, rebasing)
1807 call s:AddSection('Untracked', untracked)
1808 call s:AddSection('Unstaged', unstaged)
1809 let unstaged_end = len(unstaged) ? line('$') : 0
1810 call s:AddSection('Staged', staged)
1811 let staged_end = len(staged) ? line('$') : 0
1812 call s:AddSection('Unpushed to ' . push, unpushed)
1813 call s:AddSection('Unpulled from ' . pull, unpulled)
1815 setlocal nomodified readonly noswapfile
1816 silent doautocmd BufReadPost
1817 setlocal nomodifiable
1818 if &bufhidden ==# ''
1819 setlocal bufhidden=delete
1821 let b:dispatch = ':Gfetch --all'
1822 call fugitive#MapJumps()
1823 call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1824 call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
1825 call s:Map('n', 's', ":<C-U>execute <SID>Do('Stage',0)<CR>", '<silent>')
1826 call s:Map('x', 's', ":<C-U>execute <SID>Do('Stage',1)<CR>", '<silent>')
1827 call s:Map('n', 'u', ":<C-U>execute <SID>Do('Unstage',0)<CR>", '<silent>')
1828 call s:Map('x', 'u', ":<C-U>execute <SID>Do('Unstage',1)<CR>", '<silent>')
1829 call s:Map('n', 'U', ":exe <SID>EchoExec('reset', '-q')<CR>", '<silent>')
1830 call s:MapMotion('gu', "exe <SID>StageJump(v:count, 'Untracked', 'Unstaged')")
1831 call s:MapMotion('gU', "exe <SID>StageJump(v:count, 'Unstaged', 'Untracked')")
1832 call s:MapMotion('gs', "exe <SID>StageJump(v:count, 'Staged')")
1833 call s:MapMotion('gp', "exe <SID>StageJump(v:count, 'Unpushed')")
1834 call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
1835 call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
1836 call s:Map('n', 'C', ":<C-U>Gcommit<CR>:echohl WarningMsg<Bar>echo ':Gstatus C is deprecated in favor of cc'<Bar>echohl NONE<CR>", '<silent>')
1837 call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1838 call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
1839 call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
1840 call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
1841 call s:Map('n', ">", ":<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>", '<silent>')
1842 call s:Map('x', "=", ":<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1843 call s:Map('x', "<", ":<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1844 call s:Map('x', ">", ":<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1845 call s:Map('n', 'D', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<Bar>redraw<Bar>echohl WarningMsg<Bar> echo ':Gstatus D is deprecated in favor of dd'<Bar>echohl NONE<CR>", '<silent>')
1846 call s:Map('n', 'dd', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>", '<silent>')
1847 call s:Map('n', 'dh', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1848 call s:Map('n', 'ds', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1849 call s:Map('n', 'dp', ":<C-U>execute <SID>StageDiffEdit()<CR>", '<silent>')
1850 call s:Map('n', 'dv', ":<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>", '<silent>')
1851 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
1852 call s:Map('n', 'P', ":<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>", '<silent>')
1853 call s:Map('x', 'P', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1854 call s:Map('n', 'p', ":<C-U>if v:count<Bar>silent exe <SID>GF('pedit')<Bar>else<Bar>echoerr 'Use = for inline diff, P for :Git add/reset --patch, 1p for :pedit'<Bar>endif<CR>", '<silent>')
1855 call s:Map('x', 'p', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1856 call s:Map('n', 'I', ":<C-U>execute <SID>StagePatch(line('.'),line('.'))<CR>", '<silent>')
1857 call s:Map('x', 'I', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1858 if empty(mapcheck('q', 'n'))
1859 nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<Bar>echohl WarningMsg<Bar>echo ':Gstatus q is deprecated in favor of gq or the built-in <Lt>C-W>q'<Bar>echohl NONE<CR>
1861 call s:Map('n', 'gq', ":<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>", '<silent>')
1862 call s:Map('n', 'R', ":echohl WarningMsg<Bar>echo 'Reloading is automatic. Use :e to force'<Bar>echohl NONE<CR>", '<silent>')
1863 call s:Map('n', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1864 call s:Map('x', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1865 call s:Map('n', 'X', ":<C-U>execute <SID>StageDelete(line('.'), 0, v:count)<CR>", '<silent>')
1866 call s:Map('x', 'X', ":<C-U>execute <SID>StageDelete(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1867 call s:Map('n', 'gI', ":<C-U>execute <SID>StageIgnore(line('.'), line('.'), v:count)<CR>", '<silent>')
1868 call s:Map('x', 'gI', ":<C-U>execute <SID>StageIgnore(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1869 call s:Map('n', '.', ':<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>')
1870 call s:Map('x', '.', ':<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>')
1871 setlocal filetype=fugitive
1873 for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']]
1874 while len(getline(lnum))
1875 let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*')
1876 if has_key(expanded[section], filename)
1877 call s:StageInline('show', lnum)
1883 let b:fugitive_reltime = reltime()
1886 return 'echoerr ' . string(v:exception)
1890 function! fugitive#FileReadCmd(...) abort
1891 let amatch = a:0 ? a:1 : expand('<amatch>')
1892 let [dir, rev] = s:DirRev(amatch)
1893 let line = a:0 > 1 ? a:2 : line("'[")
1895 return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
1897 if rev !~# ':' && s:ChompDefault('', [dir, 'cat-file', '-t', rev]) =~# '^\%(commit\|tag\)$'
1898 let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev, '--')
1900 let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev)
1902 return line . 'read !' . escape(cmd, '!#%')
1905 function! fugitive#FileWriteCmd(...) abort
1906 let tmp = tempname()
1907 let amatch = a:0 ? a:1 : expand('<amatch>')
1908 let autype = a:0 > 1 ? 'Buf' : 'File'
1909 if exists('#' . autype . 'WritePre')
1910 execute s:DoAutocmd(autype . 'WritePre ' . s:fnameescape(amatch))
1913 let [dir, commit, file] = s:DirCommitFile(amatch)
1914 if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$'))
1915 return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch)
1917 silent execute "'[,']write !".fugitive#Prepare(dir, 'hash-object', '-w', '--stdin', '--').' > '.tmp
1918 let sha1 = readfile(tmp)[0]
1919 let old_mode = matchstr(s:SystemError([dir, 'ls-files', '--stage', '.' . file])[0], '^\d\+')
1921 let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
1923 let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1]
1924 let [error, exec_error] = s:SystemError([dir, 'update-index', '--index-info'], info . "\n")
1927 if exists('#' . autype . 'WritePost')
1928 execute s:DoAutocmd(autype . 'WritePost ' . s:fnameescape(amatch))
1932 return 'echoerr '.string('fugitive: '.error)
1939 function! fugitive#BufReadCmd(...) abort
1940 let amatch = a:0 ? a:1 : expand('<amatch>')
1942 let [dir, rev] = s:DirRev(amatch)
1944 return 'echo "Invalid Fugitive URL"'
1947 let b:fugitive_type = 'stage'
1949 let [b:fugitive_type, exec_error] = s:ChompError([dir, 'cat-file', '-t', rev])
1950 if exec_error && rev =~# '^:0'
1951 let sha = s:ChompDefault('', dir, 'write-tree', '--prefix=' . rev[3:-1])
1952 let exec_error = empty(sha)
1953 let b:fugitive_type = exec_error ? '' : 'tree'
1956 let error = b:fugitive_type
1957 unlet b:fugitive_type
1959 if empty(&bufhidden)
1960 setlocal bufhidden=delete
1963 let &l:readonly = !filewritable(fugitive#Find('.git/index', dir))
1964 return 'silent doautocmd BufNewFile'
1966 setlocal readonly nomodifiable
1967 return 'silent doautocmd BufNewFile|echo ' . string(error)
1969 elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
1970 return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'")
1972 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
1973 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
1977 if b:fugitive_type !=# 'blob'
1981 setlocal noreadonly modifiable
1982 let pos = getpos('.')
1983 silent keepjumps %delete_
1987 silent doautocmd BufReadPre
1988 if b:fugitive_type ==# 'tree'
1989 let b:fugitive_display_format = b:fugitive_display_format % 2
1990 if b:fugitive_display_format
1991 call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev])
1994 let sha = s:TreeChomp(dir, 'rev-parse', '--verify', rev, '--')
1996 call s:ReplaceCmd([dir, 'show', '--no-color', sha])
1998 elseif b:fugitive_type ==# 'tag'
1999 let b:fugitive_display_format = b:fugitive_display_format % 2
2000 if b:fugitive_display_format
2001 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
2003 call s:ReplaceCmd([dir, 'cat-file', '-p', rev])
2005 elseif b:fugitive_type ==# 'commit'
2006 let b:fugitive_display_format = b:fugitive_display_format % 2
2007 if b:fugitive_display_format
2008 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
2010 call s:ReplaceCmd([dir, 'show', '--no-color', '-m', '--first-parent', '--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b', rev])
2011 keepjumps call search('^parent ')
2012 if getline('.') ==# 'parent '
2013 silent keepjumps delete_
2015 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
2017 keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
2019 silent keepjumps delete_
2021 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
2024 elseif b:fugitive_type ==# 'stage'
2025 call s:ReplaceCmd([dir, 'ls-files', '--stage'])
2026 elseif b:fugitive_type ==# 'blob'
2027 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
2030 keepjumps call setpos('.',pos)
2031 setlocal nomodified noswapfile
2032 let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
2033 let &l:readonly = !modifiable || !filewritable(fugitive#Find('.git/index', dir))
2034 if empty(&bufhidden)
2035 setlocal bufhidden=delete
2037 let &l:modifiable = modifiable
2038 if b:fugitive_type !=# 'blob'
2039 setlocal filetype=git foldmethod=syntax
2040 call s:Map('n', 'a', ":<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2041 call s:Map('n', 'i', ":<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2043 call fugitive#MapJumps()
2047 return 'silent ' . s:DoAutocmd('BufReadPost') .
2048 \ (modifiable ? '' : '|setl nomodifiable')
2050 return 'echoerr ' . string(v:exception)
2054 function! fugitive#BufWriteCmd(...) abort
2055 return fugitive#FileWriteCmd(a:0 ? a:1 : expand('<amatch>'), 1)
2058 function! fugitive#SourceCmd(...) abort
2059 let amatch = a:0 ? a:1 : expand('<amatch>')
2060 let temp = s:BlobTemp(amatch)
2062 return 'noautocmd source ' . s:fnameescape(amatch)
2064 if !exists('g:virtual_scriptnames')
2065 let g:virtual_scriptnames = {}
2067 let g:virtual_scriptnames[temp] = amatch
2068 return 'source ' . s:fnameescape(temp)
2071 " Section: Temp files
2073 if !exists('s:temp_files')
2074 let s:temp_files = {}
2077 function! s:TempState(...) abort
2078 return get(s:temp_files, s:cpath(fnamemodify(a:0 ? a:1 : @%, ':p')), {})
2081 function! s:TempReadPre(file) abort
2082 if has_key(s:temp_files, s:cpath(a:file))
2083 let dict = s:temp_files[s:cpath(a:file)]
2085 setlocal bufhidden=delete nobuflisted
2086 setlocal buftype=nowrite
2087 if has_key(dict, 'modifiable')
2088 let &l:modifiable = dict.modifiable
2091 let b:git_dir = dict.dir
2092 call extend(b:, {'fugitive_type': 'temp'}, 'keep')
2097 function! s:TempReadPost(file) abort
2098 if has_key(s:temp_files, s:cpath(a:file))
2099 let dict = s:temp_files[s:cpath(a:file)]
2100 if has_key(dict, 'filetype') && dict.filetype !=# &l:filetype
2101 let &l:filetype = dict.filetype
2103 setlocal foldmarker=<<<<<<<,>>>>>>>
2104 if empty(mapcheck('q', 'n'))
2105 nnoremap <buffer> <silent> q :<C-U>bdelete<Bar>echohl WarningMsg<Bar>echo "Temp file q is deprecated in favor of the built-in <Lt>C-W>q"<Bar>echohl NONE<CR>
2108 call s:Map('n', 'gq', ":<C-U>bdelete<CR>", '<silent> <unique>')
2114 augroup fugitive_temp
2116 autocmd BufReadPre * exe s:TempReadPre( expand('<amatch>:p'))
2117 autocmd BufReadPost * exe s:TempReadPost(expand('<amatch>:p'))
2122 function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
2124 let [args, after] = s:SplitExpandChain(a:arg, s:Tree(dir))
2126 let cmd = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
2127 return (empty(cmd) ? 'exe' : cmd) . after
2129 let alias = get(s:Aliases(dir), args[0], '!')
2130 if get(args, 1, '') !=# '--help' && alias !~# '^!\|[\"'']' && !filereadable(s:ExecPath() . '/git-' . args[0])
2131 \ && !(has('win32') && filereadable(s:ExecPath() . '/git-' . args[0] . '.exe'))
2132 call remove(args, 0)
2133 call extend(args, split(alias, '\s\+'), 'keep')
2135 let name = substitute(args[0], '\%(^\|-\)\(\l\)', '\u\1', 'g')
2136 if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
2139 return 'exe ' . string(s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])) . after
2141 return 'echoerr ' . string(v:exception)
2144 if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' ||
2145 \ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
2146 \ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
2147 return s:OpenExec((a:line2 > 0 ? a:line2 : '') . (a:line2 ? 'split' : 'edit'), a:mods, args, dir) . after
2149 if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') ||
2150 \ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive') ||
2151 \ index(['--paginate', '-p'], args[0]) >= 0
2152 let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
2153 let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : ''
2155 if &autowrite || &autowriteall | silent! wall | endif
2156 return mods . (a:line2 ? 'split' : 'edit') . ' term://' . s:fnameescape(s:UserCommand(dir, args)) . assign . '|startinsert' . after
2157 elseif has('terminal')
2158 if &autowrite || &autowriteall | silent! wall | endif
2159 return 'exe ' . string(mods . 'terminal ' . (a:line2 ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . assign . after
2162 if has('gui_running') && !has('win32')
2163 call insert(args, '--no-pager')
2166 if has('nvim') && executable('env')
2167 let pre .= 'env GIT_TERMINAL_PROMPT=0 '
2169 return 'exe ' . string('noautocmd !' . escape(pre . s:UserCommand(dir, args), '!#%')) .
2170 \ '|call fugitive#ReloadStatus(' . string(dir) . ', 1)' .
2174 let s:exec_paths = {}
2175 function! s:ExecPath() abort
2176 if !has_key(s:exec_paths, g:fugitive_git_executable)
2177 let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
2179 return s:exec_paths[g:fugitive_git_executable]
2182 function! s:Subcommands() abort
2183 let exec_path = s:ExecPath()
2184 return map(split(glob(exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(exec_path)+5 : -1],"\\.exe$","")')
2188 function! s:Aliases(dir) abort
2189 if !has_key(s:aliases, a:dir)
2190 let s:aliases[a:dir] = {}
2191 let lines = s:NullError([a:dir, 'config', '-z', '--get-regexp', '^alias[.]'])[0]
2193 let s:aliases[a:dir][matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*')
2196 return s:aliases[a:dir]
2199 function! fugitive#Complete(lead, ...) abort
2200 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
2201 let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
2202 let subcmd = matchstr(pre, '\u\w*[! ] *\zs[[:alnum:]-]\+\ze ')
2204 let results = sort(s:Subcommands() + keys(s:Aliases(dir)))
2205 elseif pre =~# ' -- '
2206 return fugitive#CompletePath(a:lead, dir)
2207 elseif a:lead =~# '^-'
2208 let results = split(s:ChompDefault('', dir, subcmd, '--git-completion-helper'), ' ')
2210 return fugitive#CompleteObject(a:lead, dir)
2212 return filter(results, 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
2215 " Section: :Gcd, :Glcd
2217 function! fugitive#CdComplete(A, L, P) abort
2218 return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
2221 function! fugitive#Cd(path, ...) abort
2222 let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
2223 if path !~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
2226 let path = (empty(s:Tree(dir)) ? dir : s:Tree(dir)) . '/' . path
2228 return (a:0 && a:1 ? 'lcd ' : 'cd ') . s:fnameescape(FugitiveVimPath(path))
2233 function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
2234 let dir = a:0 ? a:1 : s:Dir()
2237 let mods = s:Mods(a:mods, &splitbelow ? 'botright' : 'topleft')
2238 let file = fugitive#Find(':', dir)
2239 let arg = ' +setl\ foldmethod=syntax\ foldlevel=1\|let\ w:fugitive_status=FugitiveGitDir() ' .
2240 \ s:fnameescape(file)
2241 for winnr in range(1, winnr('$'))
2242 if s:cpath(file, fnamemodify(bufname(winbufnr(winnr)), ':p'))
2244 call s:ReloadStatus()
2246 call s:ExpireStatus(dir)
2247 exe winnr . 'wincmd w'
2249 let w:fugitive_status = dir
2255 return mods . 'edit' . (a:bang ? '!' : '') . arg
2257 return mods . 'pedit' . arg . '|wincmd P'
2259 return mods . (a:count > 0 ? a:count : '') . 'split' . arg
2262 return 'echoerr ' . string(v:exception)
2267 function! s:StageJump(offset, section, ...) abort
2268 let line = search('^\%(' . a:section . '\)', 'nw')
2270 let line = search('^\%(' . a:1 . '\)', 'nw')
2275 for i in range(a:offset)
2276 call search(s:file_commit_pattern . '\|^$', 'W')
2277 if empty(getline('.')) && a:0 && getline(line('.') + 1) =~# '^\%(' . a:1 . '\)'
2278 call search(s:file_commit_pattern . '\|^$', 'W')
2280 if empty(getline('.'))
2284 call s:StageReveal()
2286 call s:StageReveal()
2293 function! s:StageSeek(info, fallback) abort
2295 if empty(info.section)
2298 let line = search('^' . info.section, 'wn')
2300 for section in get({'Staged': ['Unstaged', 'Untracked'], 'Unstaged': ['Untracked', 'Staged'], 'Untracked': ['Unstaged', 'Staged']}, info.section, [])
2301 let line = search('^' . section, 'wn')
2303 return line + (info.index > 0 ? 1 : 0)
2309 while len(getline(line))
2310 let filename = matchstr(getline(line), '^[A-Z?] \zs.*')
2312 \ ((info.filename[-1:-1] ==# '/' && filename[0 : len(info.filename) - 1] ==# info.filename) ||
2313 \ (filename[-1:-1] ==# '/' && filename ==# info.filename[0 : len(filename) - 1]) ||
2314 \ filename ==# info.filename)
2318 if getline(line+1) !~# '^@'
2319 exe s:StageInline('show', line)
2321 if getline(line+1) !~# '^@'
2324 let type = info.sigil ==# '-' ? '-' : '+'
2326 while offset < info.offset
2328 if getline(line) =~# '^@'
2329 let offset = +matchstr(getline(line), type . '\zs\d\+') - 1
2330 elseif getline(line) =~# '^[ ' . type . ']'
2332 elseif getline(line) !~# '^[ @\+-]'
2339 let commit = matchstr(getline(line), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\+')
2340 if len(commit) && commit ==# info.commit
2346 let i += getline(line) !~# '^[ @\+-]'
2349 return exists('backup') ? backup : line - 1
2352 function! s:DoAutocmdChanged(dir) abort
2353 let dir = a:dir is# -2 ? '' : FugitiveGitDir(a:dir)
2354 if empty(dir) || !exists('#User#FugitiveChanged') || exists('g:fugitive_event')
2358 let g:fugitive_event = dir
2359 exe s:DoAutocmd('User FugitiveChanged')
2361 unlet! g:fugitive_event
2362 " Force statusline reload with the buffer's Git dir
2368 function! s:ReloadStatusBuffer(...) abort
2369 if get(b:, 'fugitive_type', '') !=# 'index'
2372 let original_lnum = a:0 ? a:1 : line('.')
2373 let info = s:StageInfo(original_lnum)
2374 call fugitive#BufReadStatus()
2375 exe s:StageSeek(info, original_lnum)
2380 function! s:ReloadStatus(...) abort
2381 call s:ExpireStatus(-1)
2382 call s:ReloadStatusBuffer(a:0 ? a:1 : line('.'))
2383 exe s:DoAutocmdChanged(-1)
2387 let s:last_time = reltime()
2388 if !exists('s:last_times')
2389 let s:last_times = {}
2392 function! s:ExpireStatus(bufnr) abort
2394 let s:last_time = reltime()
2397 let dir = s:Dir(a:bufnr)
2399 let s:last_times[s:cpath(dir)] = reltime()
2404 function! FugitiveReloadCheck() abort
2405 let t = b:fugitive_reltime
2406 return [t, reltimestr(reltime(s:last_time, t)),
2407 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t))]
2410 function! s:ReloadWinStatus(...) abort
2411 if get(b:, 'fugitive_type', '') !=# 'index' || &modified
2414 if !exists('b:fugitive_reltime')
2415 exe s:ReloadStatusBuffer()
2418 let t = b:fugitive_reltime
2419 if reltimestr(reltime(s:last_time, t)) =~# '-\|\d\{10\}\.' ||
2420 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t)) =~# '-\|\d\{10\}\.'
2421 exe s:ReloadStatusBuffer()
2425 function! s:ReloadTabStatus(...) abort
2426 let mytab = tabpagenr()
2427 let tab = a:0 ? a:1 : mytab
2428 for winnr in range(1, tabpagewinnr(tab, '$'))
2429 if getbufvar(tabpagebuflist(tab)[winnr-1], 'fugitive_type') ==# 'index'
2430 execute 'tabnext '.tab
2432 execute winnr.'wincmd w'
2433 let restorewinnr = 1
2436 call s:ReloadWinStatus()
2438 if exists('restorewinnr')
2442 execute 'tabnext '.mytab
2446 unlet! t:fugitive_reload_status
2449 function! fugitive#ReloadStatus(...) abort
2450 call s:ExpireStatus(a:0 ? a:1 : -1)
2451 if a:0 > 1 ? a:2 : 1
2453 let t:fugitive_reload_status = t
2454 for tabnr in exists('*settabvar') ? range(1, tabpagenr('$')) : []
2455 call settabvar(tabnr, 'fugitive_reload_status', t)
2457 call s:ReloadTabStatus()
2458 exe s:DoAutocmdChanged(a:0 ? a:1 : -1)
2460 call s:ReloadWinStatus()
2465 function! fugitive#EfmDir(...) abort
2466 let dir = matchstr(a:0 ? a:1 : &errorformat, '\c,%\\&\%(git\|fugitive\)_\=dir=\zs\%(\\.\|[^,]\)*')
2467 let dir = substitute(dir, '%%', '%', 'g')
2468 let dir = substitute(dir, '\\\ze[\,]', '', 'g')
2472 augroup fugitive_status
2474 autocmd BufWritePost * call fugitive#ReloadStatus(-1, 0)
2475 autocmd ShellCmdPost,ShellFilterPost * nested call fugitive#ReloadStatus(-2, 0)
2476 autocmd BufDelete * nested
2477 \ if getbufvar(+expand('<abuf>'), 'buftype') ==# 'terminal' |
2478 \ if !empty(FugitiveGitDir(+expand('<abuf>'))) |
2479 \ call fugitive#ReloadStatus(+expand('<abuf>'), 1) |
2481 \ call fugitive#ReloadStatus(-2, 0) |
2484 autocmd QuickFixCmdPost make,lmake,[cl]file,[cl]getfile nested
2485 \ call fugitive#ReloadStatus(fugitive#EfmDir(), 1)
2487 autocmd FocusGained * call fugitive#ReloadStatus(-2, 0)
2489 autocmd BufEnter index,index.lock
2490 \ call s:ReloadWinStatus()
2492 \ if exists('t:fugitive_reload_status') |
2493 \ call s:ReloadTabStatus() |
2497 function! s:StageInfo(...) abort
2498 let lnum = a:0 ? a:1 : line('.')
2499 let sigil = matchstr(getline(lnum), '^[ @\+-]')
2502 let type = sigil ==# '-' ? '-' : '+'
2503 while lnum > 0 && getline(lnum) !~# '^@'
2504 if getline(lnum) =~# '^[ '.type.']'
2509 let offset += matchstr(getline(lnum), type.'\zs\d\+')
2510 while getline(lnum) =~# '^[ @\+-]'
2514 let slnum = lnum + 1
2517 while len(getline(slnum - 1)) && empty(section)
2519 let section = matchstr(getline(slnum), '^\u\l\+\ze.* (\d\+)$')
2520 if empty(section) && getline(slnum) !~# '^[ @\+-]'
2524 let text = matchstr(getline(lnum), '^[A-Z?] \zs.*')
2525 return {'section': section,
2526 \ 'heading': getline(slnum),
2530 \ 'relative': reverse(split(text, ' -> ')),
2531 \ 'paths': map(reverse(split(text, ' -> ')), 's:Tree() . "/" . v:val'),
2532 \ 'commit': matchstr(getline(lnum), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze '),
2533 \ 'status': matchstr(getline(lnum), '^[A-Z?]\ze \|^\%(\x\x\x\)\@!\l\+\ze [0-9a-f]'),
2534 \ 'sub': get(get(get(b:fugitive_files, section, {}), text, {}), 'sub', ''),
2538 function! s:Selection(arg1, ...) abort
2540 let arg1 = line('.')
2542 elseif a:arg1 ==# 'v'
2543 let arg1 = line("'<")
2544 let arg2 = line("'>")
2547 let arg2 = a:0 ? a:1 : 0
2551 let last = first - arg2 - 1
2557 while getline(first) =~# '^$\|^[A-Z][a-z]'
2560 if first > last || &filetype !=# 'fugitive'
2564 while getline(flnum) =~# '^[ @\+-]'
2567 let slnum = flnum + 1
2570 while len(getline(slnum - 1)) && empty(section)
2572 let heading = matchstr(getline(slnum), '^\u\l\+.* (\d\+)$')
2573 if empty(heading) && getline(slnum) !~# '^[ @\+-]'
2579 \ 'heading': heading,
2580 \ 'section': matchstr(heading, '^\u\l\+\ze.* (\d\+)$'),
2588 let line = getline(flnum)
2589 let lnum = first - (arg1 == flnum ? 0 : 1)
2590 let root = s:Tree() . '/'
2592 if line =~# '^\u\l\+\ze.* (\d\+)$'
2593 let template.heading = getline(lnum)
2594 let template.section = matchstr(template.heading, '^\u\l\+\ze.* (\d\+)$')
2595 let template.index = 0
2596 elseif line =~# '^[ @\+-]'
2597 let template.index -= 1
2598 if !results[-1].patch
2599 let results[-1].patch = lnum
2601 let results[-1].lnum = lnum
2602 elseif line =~# '^[A-Z?] '
2603 let filename = matchstr(line, '^[A-Z?] \zs.*')
2604 call add(results, extend(deepcopy(template), {
2606 \ 'filename': filename,
2607 \ 'relative': reverse(split(filename, ' -> ')),
2608 \ 'paths': map(reverse(split(filename, ' -> ')), 'root . v:val'),
2609 \ 'status': matchstr(line, '^[A-Z?]'),
2611 elseif line =~# '^\x\x\x\+ '
2612 call add(results, extend({
2614 \ 'commit': matchstr(line, '^\x\x\x\+'),
2615 \ }, template, 'keep'))
2616 elseif line =~# '^\l\+ \x\x\x\+ '
2617 call add(results, extend({
2619 \ 'commit': matchstr(line, '^\l\+ \zs\x\x\x\+'),
2620 \ 'status': matchstr(line, '^\l\+'),
2621 \ }, template, 'keep'))
2624 let template.index += 1
2625 let line = getline(lnum)
2627 if len(results) && results[0].patch && arg2 == 0
2628 while getline(results[0].patch) =~# '^[ \+-]'
2629 let results[0].patch -= 1
2631 while getline(results[0].lnum + 1) =~# '^[ \+-]'
2632 let results[0].lnum += 1
2638 function! s:StageArgs(visual) abort
2641 for record in s:Selection(a:visual ? 'v' : 'n')
2642 if len(record.commit)
2643 call add(commits, record.commit)
2645 call extend(paths, record.paths)
2647 if s:cpath(s:Tree(), getcwd())
2648 call map(paths, 'fugitive#Path(v:val, "./")')
2650 return join(map(commits + paths, 's:fnameescape(v:val)'), ' ')
2653 function! s:Do(action, visual) abort
2654 let line = getline('.')
2656 if !a:visual && !v:count && line =~# '^[A-Z][a-z]'
2657 let header = matchstr(line, '^\S\+\ze:')
2658 if len(header) && exists('*s:Do' . a:action . header . 'Header')
2659 let reload = s:Do{a:action}{header}Header(matchstr(line, ': \zs.*')) > 0
2661 let section = matchstr(line, '^\S\+')
2662 if exists('*s:Do' . a:action . section . 'Heading')
2663 let reload = s:Do{a:action}{section}Heading(line) > 0
2666 return reload ? s:ReloadStatus() : ''
2668 let selection = s:Selection(a:visual ? 'v' : 'n')
2672 call filter(selection, 'v:val.section ==# selection[0].section')
2676 for record in selection
2677 if exists('*s:Do' . a:action . record.section)
2678 let status = s:Do{a:action}{record.section}(record)
2685 let reload = reload || (status > 0)
2688 execute record.lnum + 1
2692 return 'echoerr ' . string(v:exception)
2695 execute s:ReloadStatus()
2697 if exists('success')
2698 call s:StageReveal()
2704 function! s:StageReveal() abort
2706 let begin = line('.')
2707 if getline(begin) =~# '^@'
2709 while getline(end) =~# '^[ \+-]'
2712 elseif getline(begin) =~# '^commit '
2714 while end < line('$') && getline(end + 1) !~# '^commit '
2717 elseif getline(begin) =~# s:section_pattern
2719 while len(getline(end + 1))
2724 while line('.') > line('w0') + &scrolloff && end > line('w$')
2725 execute "normal! \<C-E>"
2730 let s:file_pattern = '^[A-Z?] .\|^diff --'
2731 let s:file_commit_pattern = s:file_pattern . '\|^\%(\l\{3,\} \)\=[0-9a-f]\{4,\} '
2732 let s:item_pattern = s:file_commit_pattern . '\|^@@'
2734 function! s:NextHunk(count) abort
2735 if &filetype ==# 'fugitive' && getline('.') =~# s:file_pattern
2736 exe s:StageInline('show')
2738 for i in range(a:count)
2739 if &filetype ==# 'fugitive'
2740 call search(s:file_pattern . '\|^@', 'W')
2741 if getline('.') =~# s:file_pattern
2742 exe s:StageInline('show')
2743 if getline(line('.') + 1) =~# '^@'
2748 call search('^@@', 'W')
2751 call s:StageReveal()
2755 function! s:PreviousHunk(count) abort
2756 for i in range(a:count)
2757 if &filetype ==# 'fugitive'
2758 let lnum = search(s:file_pattern . '\|^@','Wbn')
2759 call s:StageInline('show', lnum)
2760 call search('^? .\|^@','Wb')
2762 call search('^@@', 'Wb')
2765 call s:StageReveal()
2769 function! s:NextFile(count) abort
2770 for i in range(a:count)
2771 exe s:StageInline('hide')
2772 if !search(s:file_pattern, 'W')
2776 exe s:StageInline('hide')
2780 function! s:PreviousFile(count) abort
2781 exe s:StageInline('hide')
2782 for i in range(a:count)
2783 if !search(s:file_pattern, 'Wb')
2786 exe s:StageInline('hide')
2791 function! s:NextItem(count) abort
2792 for i in range(a:count)
2793 if !search(s:item_pattern, 'W') && getline('.') !~# s:item_pattern
2794 call search('^commit ', 'W')
2797 call s:StageReveal()
2801 function! s:PreviousItem(count) abort
2802 for i in range(a:count)
2803 if !search(s:item_pattern, 'Wbe') && getline('.') !~# s:item_pattern
2804 call search('^commit ', 'Wbe')
2807 call s:StageReveal()
2811 let s:section_pattern = '^[A-Z][a-z][^:]*$'
2812 let s:section_commit_pattern = s:section_pattern . '\|^commit '
2814 function! s:NextSection(count) abort
2815 let orig = line('.')
2816 if getline('.') !~# '^commit '
2819 for i in range(a:count)
2820 if !search(s:section_commit_pattern, 'W')
2824 if getline('.') =~# s:section_commit_pattern
2825 call s:StageReveal()
2826 return getline('.') =~# s:section_pattern ? '+' : ':'
2832 function! s:PreviousSection(count) abort
2833 let orig = line('.')
2834 if getline('.') !~# '^commit '
2837 for i in range(a:count)
2838 if !search(s:section_commit_pattern . '\|\%^', 'bW')
2842 if getline('.') =~# s:section_commit_pattern || line('.') == 1
2843 call s:StageReveal()
2844 return getline('.') =~# s:section_pattern ? '+' : ':'
2850 function! s:NextSectionEnd(count) abort
2852 if empty(getline('.'))
2855 for i in range(a:count)
2856 if !search(s:section_commit_pattern, 'W')
2860 return search('^.', 'Wb')
2863 function! s:PreviousSectionEnd(count) abort
2865 for i in range(a:count)
2866 if search(s:section_commit_pattern, 'Wb') <= 1
2876 return search('^.', 'Wb')
2879 function! s:PatchSearchExpr(reverse) abort
2880 let line = getline('.')
2881 if col('.') ==# 1 && line =~# '^[+-]'
2882 if line =~# '^[+-]\{3\} '
2883 let pattern = '^[+-]\{3\} ' . substitute(escape(strpart(line, 4), '^$.*[]~\'), '^\w/', '\\w/', '') . '$'
2885 let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
2888 return '?' . escape(pattern, '/') . "\<CR>"
2890 return '/' . escape(pattern, '/?') . "\<CR>"
2893 return a:reverse ? '#' : '*'
2896 function! s:StageInline(mode, ...) abort
2897 if &filetype !=# 'fugitive'
2900 let lnum1 = a:0 ? a:1 : line('.')
2901 let lnum = lnum1 + 1
2902 if a:0 > 1 && a:2 == 0
2903 let info = s:StageInfo(lnum - 1)
2904 if empty(info.paths) && len(info.section)
2905 while len(getline(lnum))
2914 while lnum > 0 && getline(lnum) =~# '^[ @\+-]'
2917 let info = s:StageInfo(lnum)
2918 if !has_key(b:fugitive_diff, info.section)
2921 if getline(lnum + 1) =~# '^[ @\+-]'
2922 let lnum2 = lnum + 1
2923 while getline(lnum2 + 1) =~# '^[ @\+-]'
2926 if a:mode !=# 'show'
2927 setlocal modifiable noreadonly
2928 exe 'silent keepjumps ' . (lnum + 1) . ',' . lnum2 . 'delete _'
2929 call remove(b:fugitive_expanded[info.section], info.filename)
2930 setlocal nomodifiable readonly nomodified
2934 if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADMRU]$' || a:mode ==# 'hide'
2941 for line in b:fugitive_diff[info.section]
2942 if mode ==# 'await' && line[0] ==# '@'
2943 let mode = 'capture'
2945 if mode !=# 'head' && line !~# '^[ @\+-]'
2951 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '--- ' . info.relative[-1]
2953 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '+++ ' . info.relative[0]
2955 elseif mode ==# 'capture'
2956 call add(diff, line)
2957 elseif line[0] ==# '@'
2963 setlocal modifiable noreadonly
2964 silent call append(lnum, diff)
2965 let b:fugitive_expanded[info.section][info.filename] = [start, len(diff)]
2966 setlocal nomodifiable readonly nomodified
2972 function! s:NextExpandedHunk(count) abort
2973 for i in range(a:count)
2974 call s:StageInline('show', line('.'), 1)
2975 call search(s:file_pattern . '\|^@','W')
2980 function! s:StageDiff(diff) abort
2981 let lnum = line('.')
2982 let info = s:StageInfo(lnum)
2983 let prefix = info.offset > 0 ? '+' . info.offset : ''
2984 if info.sub =~# '^S'
2985 if info.section ==# 'Staged'
2986 return 'Git! diff --no-ext-diff --submodule=log --cached -- ' . info.paths[0]
2987 elseif info.sub =~# '^SC'
2988 return 'Git! diff --no-ext-diff --submodule=log -- ' . info.paths[0]
2990 return 'Git! diff --no-ext-diff --submodule=diff -- ' . info.paths[0]
2992 elseif empty(info.paths) && info.section ==# 'Staged'
2993 return 'Git! diff --no-ext-diff --cached'
2994 elseif empty(info.paths)
2995 return 'Git! diff --no-ext-diff'
2996 elseif len(info.paths) > 1
2997 execute 'Gedit' . prefix s:fnameescape(':0:' . info.paths[0])
2998 return a:diff . '! HEAD:'.s:fnameescape(info.paths[1])
2999 elseif info.section ==# 'Staged' && info.sigil ==# '-'
3000 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
3001 return a:diff . '! :0:%'
3002 elseif info.section ==# 'Staged'
3003 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
3004 return a:diff . '! @:%'
3005 elseif info.sigil ==# '-'
3006 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
3007 return a:diff . '! :(top)%'
3009 execute 'Gedit' prefix s:fnameescape(':(top)'.info.paths[0])
3014 function! s:StageDiffEdit() abort
3015 let info = s:StageInfo(line('.'))
3016 let arg = (empty(info.paths) ? s:Tree() : info.paths[0])
3017 if info.section ==# 'Staged'
3018 return 'Git! diff --no-ext-diff --cached '.s:fnameescape(arg)
3019 elseif info.status ==# '?'
3020 call s:TreeChomp('add', '--intent-to-add', '--', arg)
3021 return s:ReloadStatus()
3023 return 'Git! diff --no-ext-diff '.s:fnameescape(arg)
3027 function! s:StageApply(info, reverse, extra) abort
3028 if a:info.status ==# 'R'
3029 call s:throw('fugitive: patching renamed file not yet supported')
3031 let cmd = ['apply', '-p0', '--recount'] + a:extra
3033 let start = info.patch
3035 let lines = getline(start, end)
3036 if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
3039 while getline(end) =~# '^[-+ ]'
3041 if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
3042 call add(lines, ' ' . getline(end)[1:-1])
3045 while start > 0 && getline(start) !~# '^@'
3047 if getline(start) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
3048 call insert(lines, ' ' . getline(start)[1:-1])
3049 elseif getline(start) =~# '^@'
3050 call insert(lines, getline(start))
3054 throw 'fugitive: cold not find hunk'
3055 elseif getline(start) !~# '^@@ '
3056 throw 'fugitive: cannot apply conflict hunk'
3058 let i = b:fugitive_expanded[info.section][info.filename][0]
3060 while get(b:fugitive_diff[info.section], i, '@') !~# '^@'
3061 call add(head, b:fugitive_diff[info.section][i])
3064 call extend(lines, head, 'keep')
3065 let temp = tempname()
3066 call writefile(lines, temp)
3068 call add(cmd, '--reverse')
3070 call extend(cmd, ['--', temp])
3071 let [output, exec_error] = s:ChompError(cmd)
3075 call s:throw(output)
3078 function! s:StageDelete(lnum1, lnum2, count) abort
3082 for info in s:Selection(a:lnum1, a:lnum2)
3083 if empty(info.paths)
3086 let sub = get(get(get(b:fugitive_files, info.section, {}), info.filename, {}), 'sub')
3088 if info.status ==# 'A'
3091 if info.section ==# 'Staged'
3092 call s:TreeChomp('reset', '--', info.paths[0])
3094 if info.status =~# '[MD]'
3095 call s:TreeChomp('submodule', 'update', '--', info.paths[0])
3096 call add(restore, ':Git -C ' . info.relative[0] . ' checkout -')
3100 if info.status ==# 'D'
3101 let undo = 'Gremove'
3102 elseif info.paths[0] =~# '/$'
3103 let err .= '|echoerr ' . string('fugitive: will not delete directory ' . string(info.relative[0]))
3106 let undo = 'Gread ' . s:TreeChomp('hash-object', '-w', '--', info.paths[0])[0:10]
3109 call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
3110 elseif info.status ==# '?'
3111 call s:TreeChomp('clean', '-f', '--', info.paths[0])
3113 call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
3115 call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
3116 elseif info.status =~# '[ADU]' &&
3117 \ get(b:fugitive_files[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, {'status': ''}).status =~# '[AU]'
3118 call s:TreeChomp('checkout', info.section ==# 'Staged' ? '--ours' : '--theirs', '--', info.paths[0])
3119 elseif info.status ==# 'U'
3120 call s:TreeChomp('rm', '--', info.paths[0])
3121 elseif info.status ==# 'A'
3122 call s:TreeChomp('rm', '-f', '--', info.paths[0])
3123 elseif info.section ==# 'Unstaged'
3124 call s:TreeChomp('checkout', '--', info.paths[0])
3126 call s:TreeChomp('checkout', 'HEAD^{}', '--', info.paths[0])
3128 call add(restore, ':Gsplit ' . s:fnameescape(info.relative[0]) . '|' . undo)
3131 let err .= '|echoerr ' . string(v:exception)
3136 exe s:ReloadStatus()
3137 call s:StageReveal()
3139 return 'checktime|redraw|echomsg ' . string('To restore, ' . join(restore, '|')) . err
3141 return 'checktime|redraw' . err
3145 function! s:StageIgnore(lnum1, lnum2, count) abort
3147 for info in s:Selection(a:lnum1, a:lnum2)
3148 call extend(paths, info.relative)
3150 call map(paths, '"/" . v:val')
3151 exe 'Gsplit' (a:count ? '.gitignore' : '.git/info/exclude')
3152 let last = line('$')
3153 if last == 1 && empty(getline(1))
3154 call setline(last, paths)
3156 call append(last, paths)
3162 function! s:DoToggleHeadHeader(value) abort
3163 exe 'edit' s:fnameescape(s:Dir())
3164 call search('\C^index$', 'wc')
3167 function! s:DoStageUnpushedHeading(heading) abort
3168 let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
3172 let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
3173 call feedkeys(':Gpush ' . remote . ' ' . 'HEAD:' . branch)
3176 function! s:DoToggleUnpushedHeading(heading) abort
3177 return s:DoStageUnpushedHeading(a:heading)
3180 function! s:DoStageUnpushed(record) abort
3181 let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
3185 let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
3186 call feedkeys(':Gpush ' . remote . ' ' . a:record.commit . ':' . branch)
3189 function! s:DoToggleUnpushed(record) abort
3190 return s:DoStageUnpushed(a:record)
3193 function! s:DoUnstageUnpulledHeading(heading) abort
3194 call feedkeys(':Grebase')
3197 function! s:DoToggleUnpulledHeading(heading) abort
3198 call s:DoUnstageUnpulledHeading(a:heading)
3201 function! s:DoUnstageUnpulled(record) abort
3202 call feedkeys(':Grebase ' . a:record.commit)
3205 function! s:DoToggleUnpulled(record) abort
3206 call s:DoUnstageUnpulled(a:record)
3209 function! s:DoUnstageUnpushed(record) abort
3210 call feedkeys(':Grebase --autosquash ' . a:record.commit . '^')
3213 function! s:DoToggleStagedHeading(...) abort
3214 call s:TreeChomp('reset', '-q')
3218 function! s:DoUnstageStagedHeading(heading) abort
3219 return s:DoToggleStagedHeading(a:heading)
3222 function! s:DoToggleUnstagedHeading(...) abort
3223 call s:TreeChomp('add', '-u')
3227 function! s:DoStageUnstagedHeading(heading) abort
3228 return s:DoToggleUnstagedHeading(a:heading)
3231 function! s:DoToggleUntrackedHeading(...) abort
3232 call s:TreeChomp('add', '.')
3236 function! s:DoStageUntrackedHeading(heading) abort
3237 return s:DoToggleUntrackedHeading(a:heading)
3240 function! s:DoToggleStaged(record) abort
3242 return s:StageApply(a:record, 1, ['--cached'])
3244 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3249 function! s:DoUnstageStaged(record) abort
3250 return s:DoToggleStaged(a:record)
3253 function! s:DoToggleUnstaged(record) abort
3254 if a:record.patch && a:record.status !=# 'A'
3255 return s:StageApply(a:record, 0, ['--cached'])
3257 call s:TreeChomp(['add', '-A', '--'] + a:record.paths)
3262 function! s:DoStageUnstaged(record) abort
3263 return s:DoToggleUnstaged(a:record)
3266 function! s:DoUnstageUnstaged(record) abort
3267 if a:record.status ==# 'A'
3268 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3275 function! s:DoToggleUntracked(record) abort
3276 call s:TreeChomp(['add', '--'] + a:record.paths)
3280 function! s:DoStageUntracked(record) abort
3281 return s:DoToggleUntracked(a:record)
3284 function! s:StagePatch(lnum1,lnum2) abort
3289 for lnum in range(a:lnum1,a:lnum2)
3290 let info = s:StageInfo(lnum)
3291 if empty(info.paths) && info.section ==# 'Staged'
3292 return 'Git reset --patch'
3293 elseif empty(info.paths) && info.section ==# 'Unstaged'
3294 return 'Git add --patch'
3295 elseif empty(info.paths) && info.section ==# 'Untracked'
3296 return 'Git add --interactive'
3297 elseif empty(info.paths)
3301 if info.section ==# 'Staged'
3302 let reset += info.relative
3303 elseif info.section ==# 'Untracked'
3304 let intend += info.paths
3305 elseif info.status !~# '^D'
3306 let add += info.relative
3311 call s:TreeChomp(['add', '--intent-to-add', '--'] + intend)
3314 execute "Git add --patch -- ".join(map(add,'s:fnameescape(v:val)'))
3317 execute "Git reset --patch -- ".join(map(reset,'s:fnameescape(v:val)'))
3320 return 'echoerr ' . string(v:exception)
3322 return s:ReloadStatus()
3325 " Section: :Gcommit, :Grevert
3327 function! s:CommitInteractive(line1, line2, range, bang, mods, args, patch) abort
3328 let status = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
3329 let status = len(status) ? status . '|' : ''
3331 return status . 'if search("^Unstaged")|exe "normal >"|exe "+"|endif'
3333 return status . 'if search("^Untracked\\|^Unstaged")|exe "+"|endif'
3337 function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
3338 let mods = substitute(s:Mods(a:mods), '\C\<tab\>', '-tab', 'g')
3339 let dir = a:0 ? a:1 : s:Dir()
3340 let tree = s:Tree(dir)
3341 let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
3342 let outfile = tempname()
3345 let command = 'set GIT_EDITOR=false& '
3347 let command = 'env GIT_EDITOR=false '
3351 while get(argv, i, '--') !=# '--'
3352 if argv[i] =~# '^-[apzsneiovq].'
3353 call insert(argv, argv[i][0:1])
3354 let argv[i+1] = '-' . argv[i+1][2:-1]
3359 let command .= s:UserCommand(dir, ['commit'] + argv)
3360 if (&autowrite || &autowriteall) && !a:0
3363 if s:HasOpt(argv, '-i', '--interactive')
3364 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 0)
3365 elseif s:HasOpt(argv, '-p', '--patch')
3366 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 1)
3368 let [error_string, exec_error] = s:TempCmd(outfile, command)
3369 let errors = split(error_string, "\n")
3371 if !has('gui_running')
3375 echo join(errors, "\n")
3376 if filereadable(outfile)
3377 echo join(readfile(outfile), "\n")
3379 call fugitive#ReloadStatus(dir, 1)
3382 let error = get(errors,-2,get(errors,-1,'!'))
3383 if error =~# 'false''\=\.$'
3385 while get(argv, i, '--') !=# '--'
3386 if argv[i] =~# '^\%(-[eips]\|-[CcFm].\+\|--edit\|--interactive\|--patch\|--signoff\|--reedit-message=.*\|--reuse-message=.*\|--file=.*\|--message=.*\)$'
3387 call remove(argv, i)
3388 elseif argv[i] =~# '^\%(-[CcFm]\|--reedit-message\|--reuse-message\|--file\|--message\)$'
3389 call remove(argv, i, i + 1)
3391 if argv[i] =~# '^--cleanup\>'
3397 call insert(argv, '--no-signoff', i)
3398 call insert(argv, '--no-interactive', i)
3399 call insert(argv, '--no-edit', i)
3400 if !exists('cleanup')
3401 call insert(argv, '--cleanup=strip')
3403 call extend(argv, ['-F', msgfile], 'keep')
3404 if (bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&modified) || a:line2 == 0
3405 execute mods . 'keepalt edit' s:fnameescape(msgfile)
3406 elseif s:HasOpt(argv, '-v') || mods =~# '\<tab\>'
3407 execute mods . 'keepalt -tabedit' s:fnameescape(msgfile)
3409 execute mods . 'keepalt split' s:fnameescape(msgfile)
3411 let b:fugitive_commit_arguments = argv
3412 setlocal bufhidden=wipe filetype=gitcommit
3414 elseif empty(errors)
3415 let out = readfile(outfile)
3416 echo get(out, -1, '') =~# 'stash\|\d' ? get(out, -2, '') : get(out, -1, '')
3419 echo join(errors, "\n")
3424 return 'echoerr ' . string(v:exception)
3426 call delete(outfile)
3430 function! s:RevertSubcommand(line1, line2, range, bang, mods, args) abort
3432 let no_commit = s:HasOpt(a:args, '-n', '--no-commit', '--no-edit', '--abort', '--continue', '--quit')
3433 let cmd = s:UserCommand(dir, ['revert'] + (no_commit ? [] : ['-n']) + a:args)
3434 let [out, exec_error] = s:SystemError(cmd)
3435 call fugitive#ReloadStatus(dir, 1)
3436 if no_commit || exec_error
3437 return 'echo ' . string(substitute(out, "\n$", '', ''))
3439 return s:CommitSubcommand(a:line1, a:line2, a:range, a:bang, a:mods, [], dir)
3442 function! fugitive#CommitComplete(A, L, P) abort
3443 if a:A =~# '^--fixup=\|^--squash='
3444 let commits = s:LinesError(['log', '--pretty=format:%s', '@{upstream}..'])[0]
3445 let pre = matchstr(a:A, '^--\w*=''\=') . ':/^'
3447 call map(commits, 'pre . string(tr(v:val, "|\"^$*[]", "......."))[1:-1]')
3448 call filter(commits, 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
3451 return s:FilterEscape(map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")'), a:A)
3454 return s:CompleteSub('commit', a:A, a:L, a:P, function('fugitive#CompletePath'))
3459 function! fugitive#RevertComplete(A, L, P) abort
3460 return s:CompleteSub('revert', a:A, a:L, a:P, function('s:CompleteRevision'))
3463 function! s:FinishCommit() abort
3464 let buf = +expand('<abuf>')
3465 let args = getbufvar(buf, 'fugitive_commit_arguments')
3467 call setbufvar(buf, 'fugitive_commit_arguments', [])
3468 if getbufvar(buf, 'fugitive_commit_rebase')
3469 call setbufvar(buf, 'fugitive_commit_rebase', 0)
3470 let s:rebase_continue = s:Dir(buf)
3472 return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf))
3477 " Section: :Gmerge, :Grebase, :Gpull
3479 function! fugitive#MergeComplete(A, L, P) abort
3480 return s:CompleteSub('merge', a:A, a:L, a:P, function('s:CompleteRevision'))
3483 function! fugitive#RebaseComplete(A, L, P) abort
3484 return s:CompleteSub('rebase', a:A, a:L, a:P, function('s:CompleteRevision'))
3487 function! fugitive#PullComplete(A, L, P) abort
3488 return s:CompleteSub('pull', a:A, a:L, a:P, function('s:CompleteRemote'))
3491 function! s:RebaseSequenceAborter() abort
3492 if !exists('s:rebase_sequence_aborter')
3493 let temp = tempname() . '.sh'
3496 \ 'echo exec false | cat - "$1" > "$1.fugitive"',
3497 \ 'mv "$1.fugitive" "$1"'],
3499 let s:rebase_sequence_aborter = temp
3501 return s:rebase_sequence_aborter
3504 function! fugitive#Cwindow() abort
3505 if &buftype == 'quickfix'
3509 if &buftype == 'quickfix'
3515 let s:common_efm = ''
3517 \ . '%+Eusage:%.%#,'
3518 \ . '%+Eerror:%.%#,'
3519 \ . '%+Efatal:%.%#,'
3520 \ . '%-G%.%#%\e[K%.%#,'
3521 \ . '%-G%.%#%\r%.%\+'
3523 let s:rebase_abbrevs = {
3537 function! s:RebaseEdit(cmd, dir) abort
3538 let rebase_todo = s:fnameescape(fugitive#Find('.git/rebase-merge/git-rebase-todo', a:dir))
3540 if filereadable(rebase_todo)
3541 let new = readfile(rebase_todo)
3545 for i in range(len(new))
3546 if new[i] =~# '^\l\+\s\+[0-9a-f]\{5,\}\>'
3547 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3549 let sha_length = len(s:TreeChomp(a:dir, 'rev-parse', '--short', sha))
3551 let shortened_sha = strpart(sha, 0, sha_length)
3552 let shas[shortened_sha] = sha
3553 let new[i] = substitute(new[i], sha, shortened_sha, '')
3556 call writefile(new, rebase_todo)
3558 return a:cmd . ' +setlocal\ bufhidden=wipe\|' . escape('let b:fugitive_rebase_shas = ' . string(shas), ' ') . ' ' . rebase_todo
3561 function! s:MergeRebase(cmd, bang, mods, args, ...) abort
3562 let dir = a:0 ? a:1 : s:Dir()
3564 let mods = s:Mods(a:mods)
3565 if a:cmd =~# '^rebase' && s:HasOpt(args, '-i', '--interactive')
3566 let cmd = fugitive#Prepare(dir, '-c', 'sequence.editor=sh ' . s:RebaseSequenceAborter(), 'rebase') . ' ' . s:shellesc(args)
3567 let out = system(cmd)[0:-2]
3568 for file in ['end', 'msgnum']
3569 let file = fugitive#Find('.git/rebase-merge/' . file, dir)
3570 if !filereadable(file)
3571 return 'echoerr ' . string("fugitive: " . out)
3573 call writefile([readfile(file)[0] - 1], file)
3575 call writefile([], fugitive#Find('.git/rebase-merge/done', dir))
3579 return s:RebaseEdit(mods . 'split', dir)
3580 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--edit-todo') && filereadable(fugitive#Find('.git/rebase-merge/git-rebase-todo', dir))
3581 return s:RebaseEdit(mods . 'split', dir)
3582 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--continue') && !a:0
3583 let rdir = fugitive#Find('.git/rebase-merge', dir)
3584 let exec_error = s:ChompError([dir, 'diff-index', '--cached', '--quiet', 'HEAD', '--'])[1]
3585 if exec_error && isdirectory(rdir)
3586 if getfsize(rdir . '/amend') <= 0
3587 return 'exe ' . string(mods . 'Gcommit -n -F ' . s:fnameescape(rdir .'/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3588 elseif readfile(rdir . '/amend')[0] ==# fugitive#Head(-1, dir)
3589 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(rdir . '/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3593 let had_merge_msg = filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3596 let argv += s:AskPassArgs(dir) + ['pull', '--progress']
3598 call add(argv, a:cmd)
3600 if !s:HasOpt(args, '--no-edit', '--abort', '-m') && a:cmd !=# 'rebase'
3601 call add(argv, '--edit')
3603 if a:cmd ==# 'rebase' && s:HasOpt(args, '--autosquash') && !s:HasOpt(args, '--interactive', '-i')
3604 call add(argv, '--interactive')
3606 call extend(argv, args)
3608 let [mp, efm] = [&l:mp, &l:efm]
3610 let cdback = s:Cd(s:Tree(dir))
3611 let &l:errorformat = ''
3612 \ . '%-Gerror:%.%#false''.,'
3613 \ . '%-G%.%# ''git commit'' %.%#,'
3614 \ . '%+Emerge:%.%#,'
3615 \ . s:common_efm . ','
3616 \ . '%+ECannot %.%#: You have unstaged changes.,'
3617 \ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
3618 \ . '%+EThere is no tracking information for the current branch.,'
3619 \ . '%+EYou are not currently on a branch. Please specify which,'
3620 \ . '%+I %#git rebase --continue,'
3621 \ . 'CONFLICT (%m): %f deleted in %.%#,'
3622 \ . 'CONFLICT (%m): Merge conflict in %f,'
3623 \ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
3624 \ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
3625 \ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
3626 \ . '%+ECONFLICT %.%#,'
3627 \ . '%+EKONFLIKT %.%#,'
3628 \ . '%+ECONFLIT %.%#,'
3629 \ . "%+EXUNG \u0110\u1ed8T %.%#,"
3630 \ . "%+E\u51b2\u7a81 %.%#,"
3632 if a:cmd =~# '^merge' && empty(args) &&
3633 \ (had_merge_msg || isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
3634 \ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
3635 let cmd = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
3637 let cmd = s:UserCommand(dir, argv)
3639 if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
3640 let old_sequence_editor = $GIT_SEQUENCE_EDITOR
3641 let $GIT_SEQUENCE_EDITOR = 'true'
3643 let cmd = 'env GIT_SEQUENCE_EDITOR=true ' . cmd
3645 if !empty($GIT_EDITOR) || has('win32')
3646 let old_editor = $GIT_EDITOR
3647 let $GIT_EDITOR = 'false'
3649 let cmd = 'env GIT_EDITOR=false ' . substitute(cmd, '^env ', '', '')
3651 if !has('patch-8.1.0334') && has('terminal') && &autowrite
3652 let autowrite_was_set = 1
3656 let &l:makeprg = cmd
3657 silent noautocmd make!
3658 catch /^Vim\%((\a\+)\)\=:E211/
3659 let err = v:exception
3661 if exists('autowrite_was_set')
3665 let [&l:mp, &l:efm] = [mp, efm]
3666 if exists('old_editor')
3667 let $GIT_EDITOR = old_editor
3669 if exists('old_sequence_editor')
3670 let $GIT_SEQUENCE_EDITOR = old_sequence_editor
3674 call fugitive#ReloadStatus(dir, 1)
3675 if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
3676 if a:cmd =~# '^rebase' &&
3677 \ filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) &&
3678 \ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) &&
3679 \ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^e]'
3681 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1'
3682 elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3684 return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir))
3687 let qflist = getqflist()
3692 let e.pattern = '^<<<<<<<'
3695 call fugitive#Cwindow()
3697 call setqflist(qflist, 'r')
3703 return exists('err') ? 'echoerr '.string(err) : 'exe'
3706 function! s:RebaseClean(file) abort
3707 if !filereadable(a:file)
3710 let old = readfile(a:file)
3712 for i in range(len(new))
3713 let new[i] = substitute(new[i], '^\l\>', '\=get(s:rebase_abbrevs,submatch(0),submatch(0))', '')
3715 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3716 let rebase_shas = getbufvar(a:file, 'fugitive_rebase_shas')
3717 if len(sha) && type(rebase_shas) == type({}) && has_key(rebase_shas, sha)
3718 let new[i] = substitute(new[i], '\C\<' . sha . '\>', rebase_shas[sha], '')
3722 call writefile(new, a:file)
3727 function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort
3728 return s:MergeRebase('merge', a:bang, a:mods, a:args)
3731 function! s:RebaseSubcommand(line1, line2, range, bang, mods, args) abort
3732 return s:MergeRebase('rebase', a:bang, a:mods, a:args)
3735 function! s:PullSubcommand(line1, line2, range, bang, mods, args) abort
3736 return s:MergeRebase('pull', a:bang, a:mods, a:args)
3739 augroup fugitive_merge
3741 autocmd VimLeavePre,BufDelete git-rebase-todo
3742 \ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
3743 \ call s:RebaseClean(expand('<afile>')) |
3744 \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
3745 \ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) |
3748 autocmd BufEnter * nested
3749 \ if exists('s:rebase_continue') |
3750 \ exe s:MergeRebase('rebase', 0, '', [getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort'], remove(s:, 'rebase_continue')) |
3754 " Section: :Ggrep, :Glog
3756 if !exists('g:fugitive_summary_format')
3757 let g:fugitive_summary_format = '%s'
3760 function! fugitive#GrepComplete(A, L, P) abort
3761 return s:CompleteSub('grep', a:A, a:L, a:P)
3764 function! fugitive#LogComplete(A, L, P) abort
3765 return s:CompleteSub('log', a:A, a:L, a:P)
3768 function! s:GrepParseLine(prefix, name_only, dir, line) abort
3769 let entry = {'valid': 1}
3770 let match = matchlist(a:line, '^\(.\{-\}\):\(\d\+\):\(\d\+:\)\=\(.*\)$')
3772 let entry.module = match[1]
3773 let entry.lnum = +match[2]
3774 let entry.col = +match[3]
3775 let entry.text = match[4]
3776 elseif a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
3777 return {'text': a:line}
3779 let entry.module = matchstr(a:line, '\CBinary file \zs.*\ze matches$')
3780 if len(entry.module)
3781 let entry.text = 'Binary file'
3785 if empty(entry.module) && a:name_only
3786 let entry.module = a:line
3788 if empty(entry.module)
3789 return {'text': a:line}
3791 if entry.module !~# ':'
3792 let entry.filename = a:prefix . entry.module
3794 let entry.filename = fugitive#Find(entry.module, a:dir)
3799 function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
3802 let listnr = a:line1 == 0 ? a:line1 : a:line2
3803 let cmd = ['--no-pager', 'grep', '-n', '--no-color', '--full-name']
3804 if fugitive#GitVersion(2, 19)
3805 call add(cmd, '--column')
3807 let tree = s:Tree(dir)
3808 if type(a:args) == type([])
3809 let [args, after] = [a:args, '']
3811 let [args, after] = s:SplitExpandChain(a:args, tree)
3813 let prefix = FugitiveVimPath(s:HasOpt(args, '--cached') || empty(tree) ? 'fugitive://' . dir . '//0/' : tree . '/')
3814 let name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
3815 let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
3817 exe listnr 'wincmd w'
3822 call s:QuickfixCreate(listnr, {'title': (listnr < 0 ? ':Ggrep ' : ':Glgrep ') . s:fnameescape(args)})
3823 let tempfile = tempname()
3824 let event = listnr < 0 ? 'grep-fugitive' : 'lgrep-fugitive'
3825 silent exe s:DoAutocmd('QuickFixCmdPre ' . event)
3826 exe '!' . escape(s:UserCommand(dir, cmd + args), '%#!')
3827 \ printf(&shellpipe . (&shellpipe =~# '%s' ? '' : ' %s'), s:shellesc(tempfile))
3828 let list = map(readfile(tempfile), 's:GrepParseLine(prefix, name_only, dir, v:val)')
3829 call s:QuickfixSet(listnr, list, 'a')
3830 silent exe s:DoAutocmd('QuickFixCmdPost ' . event)
3831 if !has('gui_running')
3834 if !a:bang && !empty(list)
3835 return (listnr < 0 ? 'c' : 'l').'first' . after
3841 function! s:LogFlushQueue(state) abort
3842 let queue = remove(a:state, 'queue')
3843 if a:state.child_found
3844 call remove(queue, 0)
3846 if len(queue) && queue[-1] ==# {'text': ''}
3847 call remove(queue, -1)
3852 function! s:LogParse(state, dir, line) abort
3853 if a:state.context ==# 'hunk' && a:line =~# '^[-+ ]'
3856 let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
3858 let a:state.context = 'commit'
3859 let a:state.base = 'fugitive://' . a:dir . '//' . list[2]
3860 let a:state.base_module = len(list[1]) ? list[1] : list[2]
3861 let a:state.message = list[3]
3862 if has_key(a:state, 'diffing')
3863 call remove(a:state, 'diffing')
3865 let queue = s:LogFlushQueue(a:state)
3866 let a:state.queue = [{
3868 \ 'filename': a:state.base . a:state.target,
3869 \ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
3870 \ 'text': a:state.message}]
3871 let a:state.child_found = 0
3873 elseif type(a:line) == type(0)
3874 return s:LogFlushQueue(a:state)
3875 elseif a:line =~# '^diff'
3876 let a:state.context = 'diffhead'
3877 elseif a:line =~# '^[+-]\{3\} \w/' && a:state.context ==# 'diffhead'
3878 let a:state.diffing = a:line[5:-1]
3879 elseif a:line =~# '^@@[^@]*+\d' && has_key(a:state, 'diffing') && has_key(a:state, 'base')
3880 let a:state.context = 'hunk'
3881 if empty(a:state.target) || a:state.target ==# a:state.diffing
3882 let a:state.child_found = 1
3883 call add(a:state.queue, {
3885 \ 'filename': a:state.base . a:state.diffing,
3886 \ 'module': a:state.base_module . substitute(a:state.diffing, '^/', ':', ''),
3887 \ 'lnum': +matchstr(a:line, '+\zs\d\+'),
3888 \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
3890 elseif a:state.follow &&
3891 \ a:line =~# '^ \%(mode change \d\|\%(create\|delete\) mode \d\|\%(rename\|copy\|rewrite\) .* (\d\+%)$\)'
3892 let rename = matchstr(a:line, '^ rename \zs.* => .*\ze (\d\+%)$')
3894 let rename = rename =~# '{.* => .*}' ? rename : '{' . rename . '}'
3895 if a:state.target ==# simplify('/' . substitute(rename, '{.* => \(.*\)}', '\1', ''))
3896 let a:state.target = simplify('/' . substitute(rename, '{\(.*\) => .*}', '\1', ''))
3899 if !get(a:state, 'ignore_summary')
3900 call add(a:state.queue, {'text': a:line})
3902 elseif a:state.context ==# 'commit' || a:state.context ==# 'init'
3903 call add(a:state.queue, {'text': a:line})
3908 function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
3911 let listnr = a:type =~# '^l' ? 0 : -1
3912 let [args, after] = s:SplitExpandChain(a:args, s:Tree(dir))
3913 let split = index(args, '--')
3915 let paths = args[split : -1]
3916 let args = args[0 : split - 1]
3923 if a:line1 == 0 && a:count
3924 let path = fugitive#Path(bufname(a:count), '/', dir)
3926 let path = fugitive#Path(@%, '/', dir)
3932 let state = {'context': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
3933 if path =~# '^/\.git\%(/\|$\)\|^$'
3936 let range = "0," . (a:count ? a:count : bufnr(''))
3937 let extra = ['.' . path]
3938 if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
3939 let state.follow = 1
3940 if !s:HasOpt(args, '--follow')
3941 call insert(args, '--follow')
3943 if !s:HasOpt(args, '--summary')
3944 call insert(args, '--summary')
3945 let state.ignore_summary = 1
3949 if !s:HasOpt(args, '--merges', '--no-merges')
3950 call insert(args, '--no-merges')
3952 call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
3954 if len(path) && empty(filter(copy(args), 'v:val =~# "^[^-]"'))
3955 let owner = s:Owner(@%, dir)
3957 call add(args, owner)
3963 if s:HasOpt(args, '-g', '--walk-reflogs')
3964 let format = "%gd\t%H %gs"
3966 let format = "%h\t%H " . g:fugitive_summary_format
3968 let cmd = ['--no-pager']
3969 if fugitive#GitVersion(1, 9)
3970 call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'])
3972 call extend(cmd, ['log', '-U0', '--no-patch'])
3975 \ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
3976 \ args + paths + extra)
3977 let state.target = path
3978 let title = (listnr < 0 ? ':Gclog ' : ':Gllog ') . s:fnameescape(args + paths)
3979 if empty(paths + extra) && empty(a:type) && len(s:Relative('/'))
3980 let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after
3982 return s:QuickfixStream(listnr, title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after
3985 " Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
3987 function! s:UsableWin(nr) abort
3988 return a:nr && !getwinvar(a:nr, '&previewwindow') && !getwinvar(a:nr, '&winfixwidth') &&
3989 \ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
3990 \ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
3991 \ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
3994 function! s:OpenParse(args, wants_cmd) abort
3997 let args = copy(a:args)
3999 if args[0] =~# '^++'
4000 call add(opts, ' ' . escape(remove(args, 0), ' |"'))
4001 elseif a:wants_cmd && args[0] =~# '^+'
4002 call add(cmds, remove(args, 0)[1:-1])
4008 let file = join(args)
4009 elseif empty(expand('%'))
4011 elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>'
4017 let efile = s:Expand(file)
4018 let url = fugitive#Find(efile, dir)
4020 if a:wants_cmd && file[0] ==# '>' && efile[0] !=# '>' && get(b:, 'fugitive_type', '') isnot# 'tree' && &filetype !=# 'netrw'
4021 let line = line('.')
4022 if expand('%:p') !=# url
4023 let diffcmd = 'diff'
4024 let from = s:DirRev(@%)[1]
4025 let to = s:DirRev(url)[1]
4026 if empty(from) && empty(to)
4027 let diffcmd = 'diff-files'
4028 let args = ['--', expand('%:p'), url]
4030 let args = [from, '--', url]
4032 let args = [to, '--', expand('%:p')]
4035 let args = [from, to]
4037 let [res, exec_error] = s:LinesError([dir, diffcmd, '-U0'] + args)
4039 call filter(res, 'v:val =~# "^@@ "')
4040 call map(res, 'substitute(v:val, ''[-+]\d\+\zs '', ",1 ", "g")')
4041 call map(res, 'matchlist(v:val, ''^@@ -\(\d\+\),\(\d\+\) +\(\d\+\),\(\d\+\) @@'')[1:4]')
4042 if exists('reverse')
4043 call map(res, 'v:val[2:3] + v:val[0:1]')
4045 call filter(res, 'v:val[0] < '.line('.'))
4046 let hunk = get(res, -1, [0,0,0,0])
4047 if hunk[0] + hunk[1] > line('.')
4048 let line = hunk[2] + max([1 - hunk[3], 0])
4050 let line = hunk[2] + max([hunk[3], 1]) + line('.') - hunk[0] - max([hunk[1], 1])
4054 call insert(cmds, line)
4057 let pre = join(opts, '')
4059 let pre .= ' +' . escape(join(map(cmds, '"exe ".string(v:val)'), '|'), ' |"')
4061 let pre .= ' +' . escape(cmds[0], ' |"')
4066 function! s:DiffClose() abort
4067 let mywinnr = winnr()
4068 for winnr in [winnr('#')] + range(winnr('$'),1,-1)
4069 if winnr != mywinnr && getwinvar(winnr,'&diff')
4070 execute winnr.'wincmd w'
4080 function! s:BlurStatus() abort
4081 if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
4082 let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
4084 exe winnrs[0].'wincmd w'
4094 function! s:OpenExec(cmd, mods, args, ...) abort
4095 let dir = a:0 ? s:Dir(a:1) : s:Dir()
4096 let temp = tempname()
4097 let columns = get(g:, 'fugitive_columns', 80)
4101 let env = 'set COLUMNS=' . columns . '& '
4103 let env = 'env COLUMNS=' . columns . ' '
4105 silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
4106 \ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
4108 let temp = s:Resolve(temp)
4109 let first = join(readfile(temp, '', 2), "\n")
4110 if first =~# '\<\([[:upper:][:digit:]_-]\+(\d\+)\).*\1'
4111 let filetype = 'man'
4113 let filetype = 'git'
4115 let s:temp_files[s:cpath(temp)] = { 'dir': dir, 'filetype': filetype, 'modifiable': first =~# '^diff ' }
4119 silent execute s:Mods(a:mods) . a:cmd temp
4120 call fugitive#ReloadStatus(dir, 1)
4121 return 'echo ' . string(':!' . s:UserCommand(dir, a:args))
4124 function! fugitive#Open(cmd, bang, mods, arg, args) abort
4126 return s:OpenExec(a:cmd, a:mods, s:SplitExpand(a:arg, s:Tree()))
4129 let mods = s:Mods(a:mods)
4131 let [file, pre] = s:OpenParse(a:args, 1)
4133 return 'echoerr ' . string(v:exception)
4135 if file !~# '^\a\a\+:'
4136 let file = s:sub(file, '/$', '')
4141 return mods . a:cmd . pre . ' ' . s:fnameescape(file)
4144 function! fugitive#ReadCommand(line1, count, range, bang, mods, arg, args) abort
4145 let mods = s:Mods(a:mods)
4148 let delete = 'silent 1,' . line('$') . 'delete_|'
4149 let after = line('$')
4151 let delete = 'silent ' . a:line1 . ',' . a:count . 'delete_|'
4157 let args = s:SplitExpand(a:arg, s:Tree(dir))
4158 silent execute mods . after . 'read!' escape(s:UserCommand(dir, ['--no-pager'] + args), '!#%')
4159 execute delete . 'diffupdate'
4160 call fugitive#ReloadStatus(dir, 1)
4161 return 'redraw|echo '.string(':!'.s:UserCommand(dir, args))
4164 let [file, pre] = s:OpenParse(a:args, 0)
4166 return 'echoerr ' . string(v:exception)
4168 if file =~# '^fugitive:' && after is# 0
4169 return 'exe ' .string(mods . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
4172 exe after . 'foldopen!'
4174 return mods . after . 'read' . pre . ' ' . s:fnameescape(file) . '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')
4177 function! fugitive#ReadComplete(A, L, P) abort
4179 return fugitive#Complete(a:A, a:L, a:P)
4181 return fugitive#CompleteObject(a:A, a:L, a:P)
4185 " Section: :Gwrite, :Gwq
4187 function! fugitive#WriteCommand(line1, line2, range, bang, mods, arg, args) abort
4188 if exists('b:fugitive_commit_arguments')
4189 return 'write|bdelete'
4190 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
4192 elseif get(b:, 'fugitive_type', '') ==# 'index'
4194 elseif &buftype ==# 'nowrite' && getline(4) =~# '^+++ '
4195 let filename = getline(4)[6:-1]
4198 setlocal buftype=nowrite
4199 if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6]
4200 let [message, exec_error] = s:ChompError(['apply', '--cached', '--reverse', '--', expand('%:p')])
4202 let [message, exec_error] = s:ChompError(['apply', '--cached', '--', expand('%:p')])
4212 return 'Gedit '.fnameescape(filename)
4215 let mytab = tabpagenr()
4216 let mybufnr = bufnr('')
4218 let file = len(a:args) ? s:Generate(s:Expand(join(a:args, ' '))) : fugitive#Real(@%)
4220 return 'echoerr ' . string(v:exception)
4223 return 'echoerr '.string('fugitive: cannot determine file path')
4225 if file =~# '^fugitive:'
4226 return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
4229 let always_permitted = s:cpath(fugitive#Real(@%), file) && empty(s:DirCommitFile(@%)[1])
4230 if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
4231 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
4232 return 'echoerr v:errmsg'
4235 for nr in range(1,bufnr('$'))
4236 if fnamemodify(bufname(nr),':p') ==# file
4241 if treebufnr > 0 && treebufnr != bufnr('')
4242 let temp = tempname()
4243 silent execute 'keepalt %write '.temp
4244 for tab in [mytab] + range(1,tabpagenr('$'))
4245 for winnr in range(1,tabpagewinnr(tab,'$'))
4246 if tabpagebuflist(tab)[winnr-1] == treebufnr
4247 execute 'tabnext '.tab
4249 execute winnr.'wincmd w'
4250 let restorewinnr = 1
4253 let lnum = line('.')
4254 let last = line('$')
4255 silent execute '$read '.temp
4256 silent execute '1,'.last.'delete_'
4262 if exists('restorewinnr')
4265 execute 'tabnext '.mytab
4272 call writefile(readfile(temp,'b'),file,'b')
4275 execute 'write! '.s:fnameescape(file)
4279 let [error, exec_error] = s:ChompError(['add', '--force', '--', file])
4281 let [error, exec_error] = s:ChompError(['add', '--', file])
4284 let v:errmsg = 'fugitive: '.error
4285 return 'echoerr v:errmsg'
4287 if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
4291 let one = s:Generate(':1:'.file)
4292 let two = s:Generate(':2:'.file)
4293 let three = s:Generate(':3:'.file)
4294 for nr in range(1,bufnr('$'))
4295 let name = fnamemodify(bufname(nr), ':p')
4296 if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three)
4297 execute nr.'bdelete'
4302 let zero = s:Generate(':0:'.file)
4303 silent exe s:DoAutocmd('BufWritePost ' . s:fnameescape(zero))
4304 for tab in range(1,tabpagenr('$'))
4305 for winnr in range(1,tabpagewinnr(tab,'$'))
4306 let bufnr = tabpagebuflist(tab)[winnr-1]
4307 let bufname = fnamemodify(bufname(bufnr), ':p')
4308 if bufname ==# zero && bufnr != mybufnr
4309 execute 'tabnext '.tab
4311 execute winnr.'wincmd w'
4312 let restorewinnr = 1
4315 let lnum = line('.')
4316 let last = line('$')
4317 silent execute '$read '.s:fnameescape(file)
4318 silent execute '1,'.last.'delete_'
4323 if exists('restorewinnr')
4326 execute 'tabnext '.mytab
4332 call fugitive#ReloadStatus(-1, 1)
4336 function! fugitive#WqCommand(...) abort
4337 let bang = a:4 ? '!' : ''
4338 if exists('b:fugitive_commit_arguments')
4341 let result = call('fugitive#WriteCommand', a:000)
4342 if result =~# '^\%(write\|wq\|echoerr\)'
4343 return s:sub(result,'^write','wq')
4345 return result.'|quit'.bang
4349 augroup fugitive_commit
4351 autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute substitute(s:FinishCommit(), '\C^echoerr \(''[^'']*''\)*', 'redraw|echohl ErrorMsg|echo \1|echohl NONE', '')
4354 " Section: :Gpush, :Gfetch
4356 function! fugitive#PushComplete(A, L, P) abort
4357 return s:CompleteSub('push', a:A, a:L, a:P, function('s:CompleteRemote'))
4360 function! fugitive#FetchComplete(A, L, P) abort
4361 return s:CompleteSub('fetch', a:A, a:L, a:P, function('s:CompleteRemote'))
4364 function! s:AskPassArgs(dir) abort
4365 if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) &&
4366 \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#Config('core.askPass', a:dir))
4367 if s:executable(s:ExecPath() . '/git-gui--askpass')
4368 return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass']
4369 elseif s:executable('ssh-askpass')
4370 return ['-c', 'core.askPass=ssh-askpass']
4376 function! s:Dispatch(bang, cmd, args) abort
4378 let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
4380 let b:current_compiler = 'git'
4381 let &l:errorformat = s:common_efm .
4382 \ ',%\&git_dir=' . escape(substitute(dir, '%', '%%', 'g'), '\,')
4383 let &l:makeprg = s:UserCommand(dir, s:AskPassArgs(dir) + [a:cmd] + a:args)
4384 if exists(':Make') == 2
4388 if !has('patch-8.1.0334') && has('terminal') && &autowrite
4389 let autowrite_was_set = 1
4393 silent noautocmd make!
4395 return 'call fugitive#Cwindow()|silent ' . s:DoAutocmd('ShellCmdPost')
4398 let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
4399 if empty(cc) | unlet! b:current_compiler | endif
4400 if exists('autowrite_was_set')
4406 function! s:PushSubcommand(line1, line2, range, bang, mods, args) abort
4407 return s:Dispatch(a:bang ? '!' : '', 'push', a:args)
4410 function! s:FetchSubcommand(line1, line2, range, bang, mods, args) abort
4411 return s:Dispatch(a:bang ? '!' : '', 'fetch', a:args)
4416 augroup fugitive_diff
4418 autocmd BufWinLeave *
4419 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
4420 \ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
4422 autocmd BufWinEnter *
4423 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 1 |
4424 \ call s:diffoff() |
4428 function! s:can_diffoff(buf) abort
4429 return getwinvar(bufwinnr(a:buf), '&diff') &&
4430 \ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
4433 function! fugitive#CanDiffoff(buf) abort
4434 return s:can_diffoff(bufnr(a:buf))
4437 function! s:diff_modifier(count) abort
4438 let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
4439 if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
4441 elseif &diffopt =~# 'vertical'
4443 elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
4450 function! s:diff_window_count() abort
4452 for nr in range(1,winnr('$'))
4453 let c += getwinvar(nr,'&diff')
4458 function! s:diff_restore() abort
4459 let restore = 'setlocal nodiff noscrollbind'
4460 \ . ' scrollopt=' . &l:scrollopt
4461 \ . (&l:wrap ? ' wrap' : ' nowrap')
4462 \ . ' foldlevel=999'
4463 \ . ' foldmethod=' . &l:foldmethod
4464 \ . ' foldcolumn=' . &l:foldcolumn
4465 \ . ' foldlevel=' . &l:foldlevel
4466 \ . (&l:foldenable ? ' foldenable' : ' nofoldenable')
4467 if has('cursorbind')
4468 let restore .= (&l:cursorbind ? ' ' : ' no') . 'cursorbind'
4473 function! s:diffthis() abort
4475 let w:fugitive_diff_restore = s:diff_restore()
4480 function! s:diffoff() abort
4481 if exists('w:fugitive_diff_restore')
4482 execute w:fugitive_diff_restore
4483 unlet w:fugitive_diff_restore
4489 function! s:diffoff_all(dir) abort
4490 let curwin = winnr()
4491 for nr in range(1,winnr('$'))
4492 if getwinvar(nr,'&diff')
4494 execute nr.'wincmd w'
4495 let restorewinnr = 1
4497 if s:Dir() ==# a:dir
4502 execute curwin.'wincmd w'
4505 function! s:CompareAge(mine, theirs) abort
4506 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
4507 let mine = substitute(a:mine, '^:', '', '')
4508 let theirs = substitute(a:theirs, '^:', '', '')
4509 let my_score = get(scores, ':'.mine, 0)
4510 let their_score = get(scores, ':'.theirs, 0)
4511 if my_score || their_score
4512 return my_score < their_score ? -1 : my_score != their_score
4513 elseif mine ==# theirs
4516 let base = s:TreeChomp('merge-base', mine, theirs)
4519 elseif base ==# theirs
4522 let my_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:mine, '--')
4523 let their_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:theirs, '--')
4524 return my_time < their_time ? -1 : my_time != their_time
4527 function! s:IsConflicted() abort
4528 return len(@%) && !empty(s:ChompDefault('', 'ls-files', '--unmerged', '--', expand('%:p')))
4531 function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, args) abort
4532 let args = copy(a:args)
4534 if get(args, 0) =~# '^+'
4535 let post = remove(args, 0)[1:-1]
4537 if exists(':DiffGitCached') && empty(args)
4538 return s:Mods(a:mods) . 'DiffGitCached' . (len(post) ? '|' . post : '')
4540 let commit = s:DirCommitFile(@%)[1]
4541 if a:mods =~# '\<tab\>'
4542 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4543 let pre = 'tab split'
4545 let mods = 'keepalt ' . a:mods
4548 let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
4549 if (empty(args) || args[0] ==# ':') && a:keepfocus
4551 if empty(commit) && s:IsConflicted()
4552 let parents = [s:Relative(':2:'), s:Relative(':3:')]
4553 elseif empty(commit)
4554 let parents = [s:Relative(':0:')]
4555 elseif commit =~# '^\d\=$'
4556 let parents = [s:Relative('HEAD:')]
4557 elseif commit =~# '^\x\x\+$'
4558 let parents = s:LinesError(['rev-parse', commit . '^@'])[0]
4559 call map(parents, 's:Relative(v:val . ":")')
4563 if exists('parents') && len(parents) > 1
4565 let mods = (a:autodir ? s:diff_modifier(len(parents) + 1) : '') . s:Mods(mods, 'leftabove')
4567 execute mods 'split' s:fnameescape(s:Generate(parents[0]))
4568 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4572 call s:Map('n', 'd2o', ':diffget '.nr2.'<Bar>diffupdate<CR>', '<silent>')
4573 let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
4574 for i in range(len(parents)-1, 1, -1)
4575 execute mods 'split' s:fnameescape(s:Generate(parents[i]))
4576 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4580 call s:Map('n', 'd' . (i + 2) . 'o', ':diffget '.nrx.'<Bar>diffupdate<CR>', '<silent>')
4588 let arg = join(args, ' ')
4593 let file = s:Relative()
4596 let file = s:Relative(':0:')
4597 elseif arg =~# '^:\d$'
4599 let file = s:Relative(arg . ':')
4602 let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
4604 return 'echoerr ' . string(v:exception)
4607 elseif exists('parents') && len(parents)
4608 let file = parents[-1]
4610 let file = s:Relative()
4611 elseif s:IsConflicted()
4612 let file = s:Relative(':1:')
4613 let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
4616 let file = s:Relative(':0:')
4618 let spec = s:Generate(file)
4619 if spec =~# '^fugitive:' && empty(s:DirCommitFile(spec)[2])
4620 let spec = FugitiveVimPath(spec . s:Relative('/'))
4623 let restore = s:diff_restore()
4624 let w:fugitive_diff_restore = restore
4625 if len(spec) && s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
4626 let mods = s:Mods(mods, 'rightbelow')
4628 let mods = s:Mods(mods, 'leftabove')
4630 let mods = (a:autodir ? s:diff_modifier(2) : '') . mods
4631 if &diffopt =~# 'vertical'
4632 let diffopt = &diffopt
4633 set diffopt-=vertical
4635 execute mods 'diffsplit' s:fnameescape(spec)
4636 let &l:readonly = &l:readonly
4638 let w:fugitive_diff_restore = restore
4640 if getwinvar('#', '&diff')
4647 return 'echoerr ' . string(v:exception)
4649 if exists('diffopt')
4650 let &diffopt = diffopt
4655 " Section: :Gmove, :Gremove
4657 function! s:Move(force, rename, destination) abort
4660 if s:DirCommitFile(@%)[1] !~# '^0\=$' || empty(@%)
4661 return 'echoerr ' . string('fugitive: mv not supported for this buffer')
4663 if a:destination =~# '^\.\.\=\%(/\|$\)'
4664 let destination = simplify(getcwd() . '/' . a:destination)
4665 elseif a:destination =~# '^\a\+:\|^/'
4666 let destination = a:destination
4667 elseif a:destination =~# '^:/:\='
4668 let destination = s:Tree(dir) . substitute(a:destination, '^:/:\=', '', '')
4669 elseif a:destination =~# '^:(\%(top\|top,literal\|literal,top\))'
4670 let destination = s:Tree(dir) . matchstr(a:destination, ')\zs.*')
4671 elseif a:destination =~# '^:(literal)'
4672 let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
4674 let destination = expand('%:p:s?[\/]$??:h') . '/' . a:destination
4676 let destination = s:Tree(dir) . '/' . a:destination
4678 let destination = s:Slash(destination)
4682 let [message, exec_error] = s:ChompError(['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination], dir)
4684 let v:errmsg = 'fugitive: '.message
4685 return 'echoerr v:errmsg'
4687 if isdirectory(destination)
4688 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
4690 let reload = '|call fugitive#ReloadStatus(' . string(dir) . ', 1)'
4691 if empty(s:DirCommitFile(@%)[1])
4692 if isdirectory(destination)
4693 return 'keepalt edit '.s:fnameescape(destination) . reload
4695 return 'keepalt saveas! '.s:fnameescape(destination) . reload
4698 return 'file '.s:fnameescape(fugitive#Find(':0:'.destination, dir)) . reload
4702 function! fugitive#RenameComplete(A,L,P) abort
4703 if a:A =~# '^[.:]\=/'
4704 return fugitive#CompletePath(a:A)
4706 let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
4707 return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
4711 function! fugitive#MoveCommand(line1, line2, range, bang, mods, arg, args) abort
4712 return s:Move(a:bang, 0, a:arg)
4715 function! fugitive#RenameCommand(line1, line2, range, bang, mods, arg, args) abort
4716 return s:Move(a:bang, 1, a:arg)
4719 function! s:Remove(after, force) abort
4722 if len(@%) && s:DirCommitFile(@%)[1] ==# ''
4724 elseif s:DirCommitFile(@%)[1] ==# '0'
4725 let cmd = ['rm','--cached']
4727 return 'echoerr ' . string('fugitive: rm not supported for this buffer')
4730 let cmd += ['--force']
4732 let [message, exec_error] = s:ChompError(cmd + ['--', expand('%:p')], dir)
4734 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
4735 return 'echoerr '.string(v:errmsg)
4737 return a:after . (a:force ? '!' : ''). '|call fugitive#ReloadStatus(' . string(dir) . ', 1)'
4741 function! fugitive#RemoveCommand(line1, line2, range, bang, mods, arg, args) abort
4742 return s:Remove('edit', a:bang)
4745 function! fugitive#DeleteCommand(line1, line2, range, bang, mods, arg, args) abort
4746 return s:Remove('bdelete', a:bang)
4751 function! s:Keywordprg() abort
4752 let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
4753 if has('gui_running') && !has('win32')
4754 return s:UserCommand() . ' --no-pager' . args . ' log -1'
4756 return s:UserCommand() . args . ' show'
4760 function! s:linechars(pattern) abort
4761 let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
4762 if exists('*synconcealed') && &conceallevel > 1
4763 for col in range(1, chars)
4764 let chars -= synconcealed(line('.'), col)[0]
4770 function! s:BlameBufnr(...) abort
4771 let state = s:TempState(bufname(a:0 ? a:1 : ''))
4772 if get(state, 'filetype', '') ==# 'fugitiveblame'
4773 return get(state, 'bufnr', -1)
4779 function! s:BlameCommitFileLnum(...) abort
4780 let line = a:0 ? a:1 : getline('.')
4781 let state = a:0 ? a:2 : s:TempState()
4782 let commit = matchstr(line, '^\^\=\zs\x\+')
4783 if commit =~# '^0\+$'
4785 elseif has_key(state, 'blame_reverse_end')
4786 let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '')
4788 let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
4789 let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)')
4790 if empty(path) && lnum
4791 let path = get(state, 'blame_file', '')
4793 return [commit, path, lnum]
4796 function! s:BlameLeave() abort
4797 let bufwinnr = bufwinnr(s:BlameBufnr())
4799 let bufnr = bufnr('')
4800 exe bufwinnr . 'wincmd w'
4801 return bufnr . 'bdelete'
4806 function! s:BlameQuit() abort
4807 let cmd = s:BlameLeave()
4810 elseif len(s:DirCommitFile(@%)[1])
4811 return cmd . '|Gedit'
4817 function! fugitive#BlameComplete(A, L, P) abort
4818 return s:CompleteSub('blame', a:A, a:L, a:P)
4821 function! s:BlameSubcommand(line1, count, range, bang, mods, args) abort
4823 let flags = copy(a:args)
4829 if a:line1 > 0 && a:count > 0 && a:range != 1
4830 call extend(ranges, ['-L', a:line1 . ',' . a:count])
4832 while i < len(flags)
4833 let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
4834 if len(match) && len(match[2])
4835 call insert(flags, match[1])
4836 let flags[i+1] = '-' . match[2]
4840 if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$'
4842 elseif arg ==# '--contents' && i + 1 < len(flags)
4843 call extend(commits, remove(flags, i, i+1))
4845 elseif arg ==# '-L' && i + 1 < len(flags)
4846 call extend(ranges, remove(flags, i, i+1))
4848 elseif arg =~# '^--contents='
4849 call add(commits, remove(flags, i))
4851 elseif arg =~# '^-L.'
4852 call add(ranges, remove(flags, i))
4854 elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\|ignore-rev\|ignore-revs-file\)$'
4858 echo s:ChompError(['blame', arg])[0]
4863 if i + 1 < len(flags)
4864 call extend(files, remove(flags, i + 1, -1))
4866 call remove(flags, i)
4868 elseif arg !~# '^-' && (s:HasOpt(flags, '--not') || arg !~# '^\^')
4869 if index(flags, '--') >= 0
4870 call add(commits, remove(flags, i))
4873 if arg =~# '\.\.' && arg !~# '^\.\.\=\%(/\|$\)' && empty(commits)
4874 call add(commits, remove(flags, i))
4878 let dcf = s:DirCommitFile(fugitive#Find(arg))
4879 if len(dcf[1]) && empty(dcf[2])
4880 call add(commits, remove(flags, i))
4885 call add(files, remove(flags, i))
4890 let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./'))), '^\.\%(/\|$\)', '', '')
4891 if empty(commits) && len(files) > 1
4892 call add(commits, remove(files, 1))
4896 let cmd = ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', 'blame', '--show-number']
4897 call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
4898 if a:count > 0 && empty(ranges)
4899 let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
4901 call extend(cmd, ranges)
4904 elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$'))
4905 let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')]
4906 elseif empty(files) && !s:HasOpt(flags, '--reverse')
4907 let cmd += ['--contents', '-']
4909 let basecmd = escape(fugitive#Prepare(cmd) . ' -- ' . s:shellesc(len(files) ? files : file), '!#%')
4910 let tempname = tempname()
4911 let error = tempname . '.err'
4912 let temp = tempname . (raw ? '' : '.fugitiveblame')
4914 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
4916 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
4918 let l:shell_error = v:shell_error
4922 let lines = readfile(error)
4924 let lines = readfile(temp)
4926 for i in range(len(lines))
4927 if lines[i] =~# '^error: \|^fatal: '
4935 if i != len(lines) - 1
4941 let temp_state = {'dir': s:Dir(), 'filetype': (raw ? '' : 'fugitiveblame'), 'blame_flags': flags, 'blame_file': file, 'modifiable': 0}
4942 if s:HasOpt(flags, '--reverse')
4943 let temp_state.blame_reverse_end = matchstr(get(commits, 0, ''), '\.\.\zs.*')
4945 if (a:line1 == 0 || a:range == 1) && a:count > 0
4946 let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split')
4947 return s:BlameCommit(edit, get(readfile(temp), 0, ''), temp_state)
4949 let temp = s:Resolve(temp)
4950 let s:temp_files[s:cpath(temp)] = temp_state
4951 if len(ranges + commits + files) || raw
4952 let mods = s:Mods(a:mods)
4954 exe 'silent keepalt' mods 'split' s:fnameescape(temp)
4955 elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden)
4956 exe 'silent' mods 'edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp)
4958 return mods . 'edit ' . s:fnameescape(temp)
4962 if a:mods =~# '\<tab\>'
4965 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4966 for winnr in range(winnr('$'),1,-1)
4967 if getwinvar(winnr, '&scrollbind')
4968 call setwinvar(winnr, '&scrollbind', 0)
4970 if exists('+cursorbind') && getwinvar(winnr, '&cursorbind')
4971 call setwinvar(winnr, '&cursorbind', 0)
4973 if s:BlameBufnr(winbufnr(winnr)) > 0
4974 execute winbufnr(winnr).'bdelete'
4977 let bufnr = bufnr('')
4978 let temp_state.bufnr = bufnr
4979 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
4980 if exists('+cursorbind')
4981 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
4984 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
4987 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
4989 setlocal scrollbind nowrap nofoldenable
4990 if exists('+cursorbind')
4993 let top = line('w0') + &scrolloff
4994 let current = line('.')
4995 exe 'silent keepalt' (a:bang ? s:Mods(mods) . 'split' : s:Mods(mods, 'leftabove') . 'vsplit') s:fnameescape(temp)
4996 let w:fugitive_leave = restore
5000 if exists('+cursorbind')
5003 setlocal nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth
5004 if exists('+relativenumber')
5005 setlocal norelativenumber
5007 execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
5008 call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>')
5009 call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>')
5010 call s:Map('n', 'D', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze\\d\\ze\\s\\+\\d\\+)')+1-v:count)<CR>", '<silent>')
5017 return 'echoerr ' . string(v:exception)
5021 function! s:BlameCommit(cmd, ...) abort
5022 let line = a:0 ? a:1 : getline('.')
5023 let state = a:0 ? a:2 : s:TempState()
5024 let sigil = has_key(state, 'blame_reverse_end') ? '-' : '+'
5025 let mods = (s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ")
5026 let [commit, path, lnum] = s:BlameCommitFileLnum(line, state)
5027 if empty(commit) && len(path) && has_key(state, 'blame_reverse_end')
5028 let path = (len(state.blame_reverse_end) ? state.blame_reverse_end . ':' : ':(top)') . path
5029 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
5031 if commit =~# '^0*$'
5032 return 'echoerr ' . string('fugitive: no commit')
5034 if line =~# '^\^' && !has_key(state, 'blame_reverse_end')
5035 let path = commit . ':' . path
5036 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
5038 let cmd = fugitive#Open(mods . a:cmd, 0, '', commit, [commit])
5039 if cmd =~# '^echoerr'
5043 if a:cmd ==# 'pedit' || empty(path)
5046 if search('^diff .* b/\M'.escape(path,'\').'$','W')
5048 let head = line('.')
5049 while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
5050 let top = +matchstr(getline('.'),' ' . sigil .'\zs\d\+')
5051 let len = +matchstr(getline('.'),' ' . sigil . '\d\+,\zs\d\+')
5052 if lnum >= top && lnum <= top + len
5053 let offset = lnum - top
5061 while offset > 0 && line('.') < line('$')
5063 if getline('.') =~# '^[ ' . sigil . ']'
5076 function! s:BlameJump(suffix, ...) abort
5077 let suffix = a:suffix
5078 let [commit, path, lnum] = s:BlameCommitFileLnum()
5080 return 'echoerr ' . string('fugitive: could not determine filename for blame')
5082 if commit =~# '^0*$'
5086 let offset = line('.') - line('w0')
5087 let flags = get(s:TempState(), 'blame_flags', [])
5089 if s:HasOpt(flags, '--reverse')
5090 call remove(flags, '--reverse')
5092 call add(flags, '--reverse')
5095 let blame_bufnr = s:BlameBufnr()
5097 let bufnr = bufnr('')
5098 let winnr = bufwinnr(blame_bufnr)
5100 exe winnr.'wincmd w'
5102 execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
5108 if exists(':Gblame')
5109 let my_bufnr = bufnr('')
5111 let blame_args = flags + [commit . suffix, '--', path]
5112 let result = s:BlameSubcommand(0, 0, 0, 0, '', blame_args)
5114 let blame_args = flags
5115 let result = s:BlameSubcommand(-1, -1, 0, 0, '', blame_args)
5117 if bufnr('') == my_bufnr
5122 let delta = line('.') - line('w0') - offset
5124 execute 'normal! '.delta."\<C-E>"
5126 execute 'normal! '.(-delta)."\<C-Y>"
5130 echo ':Gblame' s:fnameescape(blame_args)
5135 let s:hash_colors = {}
5137 function! fugitive#BlameSyntax() abort
5138 let conceal = has('conceal') ? ' conceal' : ''
5139 let config = fugitive#Config()
5140 let flags = get(s:TempState(), 'blame_flags', [])
5141 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
5142 syn match FugitiveblameHash "\%(^\^\=[?*]*\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5143 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5144 if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$' || s:HasOpt(flags, '-b')
5145 syn match FugitiveblameBoundaryIgnore "^\^[*?]*\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5147 syn match FugitiveblameBoundary "^\^"
5149 syn match FugitiveblameScoreDebug " *\d\+\s\+\d\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile contained skipwhite
5150 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
5151 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
5152 exec 'syn match FugitiveblameLineNumber "\s*\d\+)\@=" contained containedin=FugitiveblameAnnotation' conceal
5153 exec 'syn match FugitiveblameOriginalFile "\s\%(\f\+\D\@<=\|\D\@=\f\+\)\%(\%(\s\+\d\+\)\=\s\%((\|\s*\d\+)\)\)\@=" contained nextgroup=FugitiveblameOriginalLineNumber,FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-name', '-f') ? '' : conceal)
5154 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5155 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5156 syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
5157 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
5158 hi def link FugitiveblameBoundary Keyword
5159 hi def link FugitiveblameHash Identifier
5160 hi def link FugitiveblameBoundaryIgnore Ignore
5161 hi def link FugitiveblameUncommitted Ignore
5162 hi def link FugitiveblameScoreDebug Debug
5163 hi def link FugitiveblameTime PreProc
5164 hi def link FugitiveblameLineNumber Number
5165 hi def link FugitiveblameOriginalFile String
5166 hi def link FugitiveblameOriginalLineNumber Float
5167 hi def link FugitiveblameShort FugitiveblameDelimiter
5168 hi def link FugitiveblameDelimiter Delimiter
5169 hi def link FugitiveblameNotCommittedYet Comment
5170 if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
5174 for lnum in range(1, line('$'))
5175 let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
5176 if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
5180 if &t_Co > 16 && get(g:, 'CSApprox_loaded') && !empty(findfile('autoload/csapprox/per_component.vim', escape(&rtp, ' ')))
5181 \ && empty(get(s:hash_colors, hash))
5182 let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
5183 let color = csapprox#per_component#Approximate(r, g, b)
5184 if color == 16 && &background ==# 'dark'
5187 let s:hash_colors[hash] = ' ctermfg='.color
5189 let s:hash_colors[hash] = ''
5191 exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
5193 call s:BlameRehighlight()
5196 function! s:BlameRehighlight() abort
5197 for [hash, cterm] in items(s:hash_colors)
5198 if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
5199 exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
5201 exe 'hi link FugitiveblameHash'.hash.' Identifier'
5206 function! s:BlameFileType() abort
5208 setlocal foldmethod=manual
5210 let &l:keywordprg = s:Keywordprg()
5212 let b:undo_ftplugin = 'setl keywordprg= foldmethod<'
5213 if exists('+concealcursor')
5214 setlocal concealcursor=nc conceallevel=2
5215 let b:undo_ftplugin .= ' concealcursor< conceallevel<'
5220 call s:Map('n', '<F1>', ':help :Gblame<CR>', '<silent>')
5221 call s:Map('n', 'g?', ':help :Gblame<CR>', '<silent>')
5222 if mapcheck('q', 'n') =~# '^$\|bdelete'
5223 call s:Map('n', 'q', ':exe <SID>BlameQuit()<Bar>echohl WarningMsg<Bar>echo ":Gblame q is deprecated in favor of gq"<Bar>echohl NONE<CR>', '<silent>')
5225 call s:Map('n', 'gq', ':exe <SID>BlameQuit()<CR>', '<silent>')
5226 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5227 call s:Map('n', '<CR>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5228 call s:Map('n', '-', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>')
5229 call s:Map('n', 'P', ':<C-U>exe <SID>BlameJump("^".v:count1)<CR>', '<silent>')
5230 call s:Map('n', '~', ':<C-U>exe <SID>BlameJump("~".v:count1)<CR>', '<silent>')
5231 call s:Map('n', 'i', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5232 call s:Map('n', 'o', ':<C-U>exe <SID>BlameCommit("split")<CR>', '<silent>')
5233 call s:Map('n', 'O', ':<C-U>exe <SID>BlameCommit("tabedit")<CR>', '<silent>')
5234 call s:Map('n', 'p', ':<C-U>exe <SID>BlameCommit("pedit")<CR>', '<silent>')
5237 augroup fugitive_blame
5239 autocmd FileType fugitiveblame call s:BlameFileType()
5240 autocmd ColorScheme,GUIEnter * call s:BlameRehighlight()
5241 autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
5246 let s:redirects = {}
5248 function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, args) abort
5252 let validremote = '\.\|\.\=/.*\|[[:alnum:]_-]\+\%(://.\{-\}\)\='
5255 return 'echoerr ' . string('fugitive: ''-'' no longer required to get persistent URL if range given')
5257 return 'echoerr ' . string('fugitive: use :0Gbrowse instead of :Gbrowse -')
5260 let remote = matchstr(join(a:args, ' '),'@\zs\%('.validremote.'\)$')
5261 let rev = substitute(join(a:args, ' '),'@\%('.validremote.'\)$','','')
5267 let rev = s:DirRev(@%)[1]
5270 let expanded = s:Relative()
5272 let expanded = s:Expand(rev)
5274 let cdir = FugitiveVimPath(fugitive#CommonDir(dir))
5275 for subdir in ['tags/', 'heads/', 'remotes/']
5276 if expanded !~# '^[./]' && filereadable(cdir . '/refs/' . subdir . expanded)
5277 let expanded = '.git/refs/' . subdir . expanded
5280 let full = fugitive#Find(expanded, dir)
5282 if full =~? '^fugitive:'
5283 let [pathdir, commit, path] = s:DirCommitFile(full)
5284 if commit =~# '^:\=\d$'
5288 let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':'))
5289 let branch = matchstr(expanded, '^[^:]*')
5293 let path = path[1:-1]
5294 elseif empty(s:Tree(dir))
5295 let path = '.git/' . full[strlen(dir)+1:-1]
5298 let path = fugitive#Path(full, '/')[1:-1]
5299 if path =~# '^\.git/'
5301 elseif isdirectory(full) || empty(path)
5307 if type ==# 'tree' && !empty(path)
5308 let path = s:sub(path, '/\=$', '/')
5310 if path =~# '^\.git/.*HEAD$' && filereadable(dir . '/' . path[5:-1])
5311 let body = readfile(dir . '/' . path[5:-1])[0]
5312 if body =~# '^\x\{40,\}$'
5316 elseif body =~# '^ref: refs/'
5317 let path = '.git/' . matchstr(body,'ref: \zs.*')
5322 if path =~# '^\.git/refs/remotes/.'
5324 let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+')
5325 let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5327 let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5328 let path = '.git/refs/heads/'.merge
5330 elseif path =~# '^\.git/refs/heads/.'
5331 let branch = path[16:-1]
5332 elseif !exists('branch')
5333 let branch = FugitiveHead()
5336 let r = fugitive#Config('branch.'.branch.'.remote')
5337 let m = fugitive#Config('branch.'.branch.'.merge')[11:-1]
5338 if r ==# '.' && !empty(m)
5339 let r2 = fugitive#Config('branch.'.m.'.remote')
5342 let m = fugitive#Config('branch.'.m.'.merge')[11:-1]
5348 if r ==# '.' || r ==# remote
5350 if path =~# '^\.git/refs/heads/.'
5351 let path = '.git/refs/heads/'.merge
5356 let line1 = a:count > 0 ? a:line1 : 0
5357 let line2 = a:count > 0 ? a:count : 0
5358 if empty(commit) && path !~# '^\.git/'
5359 if a:count < 0 && !empty(merge)
5364 let owner = s:Owner(@%)
5365 let [commit, exec_error] = s:ChompError(['merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? 'HEAD' : owner, '--'])
5369 if a:count > 0 && empty(a:args) && commit =~# '^\x\{40,\}$'
5370 let blame_list = tempname()
5371 call writefile([commit, ''], blame_list, 'b')
5372 let blame_in = tempname()
5373 silent exe '%write' blame_in
5374 let [blame, exec_error] = s:LinesError(['-c', 'blame.coloring=none', 'blame', '--contents', blame_in, '-L', a:line1.','.a:count, '-S', blame_list, '-s', '--show-number', './' . path])
5376 let blame_regex = '^\^\x\+\s\+\zs\d\+\ze\s'
5377 if get(blame, 0) =~# blame_regex && get(blame, -1) =~# blame_regex
5378 let line1 = +matchstr(blame[0], blame_regex)
5379 let line2 = +matchstr(blame[-1], blame_regex)
5381 call s:throw("Can't browse to uncommitted change")
5388 let commit = readfile(fugitive#Find('.git/HEAD', dir), '', 1)[0]
5391 while commit =~# '^ref: ' && i < 10
5392 let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
5400 let raw = fugitive#RemoteUrl(remote)
5405 if raw =~# '^https\=://' && s:executable('curl')
5406 if !has_key(s:redirects, raw)
5407 let s:redirects[raw] = matchstr(system('curl -I ' .
5408 \ s:shellesc(raw . '/info/refs?service=git-upload-pack')),
5409 \ 'Location: \zs\S\+\ze/info/refs?')
5411 if len(s:redirects[raw])
5412 let raw = s:redirects[raw]
5418 \ 'repo': fugitive#repo(dir),
5420 \ 'revision': 'No longer provided',
5428 for Handler in get(g:, 'fugitive_browse_handlers', [])
5429 let url = call(Handler, [copy(opts)])
5436 call s:throw("No Gbrowse handler installed for '".raw."'")
5439 let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))')
5444 return 'echomsg '.string(url)
5445 elseif exists(':Browse') == 2
5446 return 'echomsg '.string(url).'|Browse '.url
5448 if !exists('g:loaded_netrw')
5449 runtime! autoload/netrw.vim
5451 if exists('*netrw#BrowseX')
5452 return 'echomsg '.string(url).'|call netrw#BrowseX('.string(url).', 0)'
5454 return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
5458 return 'echoerr ' . string(v:exception)
5462 " Section: Go to file
5464 nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
5465 function! fugitive#MapCfile(...) abort
5466 exe 'cnoremap <buffer> <expr> <Plug><cfile>' (a:0 ? a:1 : 'fugitive#Cfile()')
5467 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap <buffer> <Plug><cfile>"'
5468 if !exists('g:fugitive_no_maps')
5469 call s:Map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>', 1)
5470 call s:Map('n', '<C-W>f', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5471 call s:Map('n', '<C-W><C-F>', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5472 call s:Map('n', '<C-W>gf', '<SID>:tabfind <Plug><cfile><CR>', '<silent><unique>', 1)
5473 call s:Map('c', '<C-R><C-F>', '<Plug><cfile>', '<silent><unique>', 1)
5477 function! s:ContainingCommit() abort
5478 let commit = s:Owner(@%)
5479 return empty(commit) ? 'HEAD' : commit
5482 function! s:SquashArgument(...) abort
5483 if &filetype == 'fugitive'
5484 let commit = matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze ')
5485 elseif has_key(s:temp_files, s:cpath(expand('%:p')))
5486 let commit = matchstr(getline('.'), '\<\x\{4,\}\>')
5488 let commit = s:Owner(@%)
5490 return len(commit) && a:0 ? printf(a:1, commit) : commit
5493 function! s:RebaseArgument() abort
5494 return s:SquashArgument(' %s^')
5497 function! s:NavigateUp(count) abort
5498 let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
5502 let rev = matchstr(rev, '.*\ze/.\+', '')
5503 elseif rev =~# '.:.'
5504 let rev = matchstr(rev, '^.[^:]*:')
5517 function! s:MapMotion(lhs, rhs) abort
5518 call s:Map('n', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5519 call s:Map('o', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5520 call s:Map('x', a:lhs, ":<C-U>exe 'normal! gv'<Bar>" . a:rhs . "<CR>", "<silent>")
5523 function! fugitive#MapJumps(...) abort
5525 if get(b:, 'fugitive_type', '') ==# 'blob'
5526 let blame_map = 'Gblame<C-R>=v:count ? " --reverse" : ""<CR><CR>'
5527 call s:Map('n', '<2-LeftMouse>', ':<C-U>0,1' . blame_map, '<silent>')
5528 call s:Map('n', '<CR>', ':<C-U>0,1' . blame_map, '<silent>')
5529 call s:Map('n', 'o', ':<C-U>0,2' . blame_map, '<silent>')
5530 call s:Map('n', 'p', ':<C-U>0,3' . blame_map, '<silent>')
5531 call s:Map('n', 'gO', ':<C-U>0,4' . blame_map, '<silent>')
5532 call s:Map('n', 'O', ':<C-U>0,5' . blame_map, '<silent>')
5534 call s:Map('n', 'D', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<Bar>redraw<Bar>echohl WarningMsg<Bar> echo ':Gstatus D is deprecated in favor of dd'<Bar>echohl NONE<CR>", '<silent>')
5535 call s:Map('n', 'dd', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<CR>", '<silent>')
5536 call s:Map('n', 'dh', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5537 call s:Map('n', 'ds', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5538 call s:Map('n', 'dv', ":<C-U>call <SID>DiffClose()<Bar>Gvdiffsplit!<CR>", '<silent>')
5539 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
5542 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5543 call s:Map('n', '<CR>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5544 call s:Map('n', 'o', ':<C-U>exe <SID>GF("split")<CR>', '<silent>')
5545 call s:Map('n', 'gO', ':<C-U>exe <SID>GF("vsplit")<CR>', '<silent>')
5546 call s:Map('n', 'O', ':<C-U>exe <SID>GF("tabedit")<CR>', '<silent>')
5547 call s:Map('n', 'p', ':<C-U>exe <SID>GF("pedit")<CR>', '<silent>')
5549 if !exists('g:fugitive_no_maps')
5550 if exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^<c-p>$'
5551 nnoremap <buffer> <silent> <C-P> :<C-U>execute line('.') == 1 ? 'CtrlP ' . fnameescape(<SID>Tree()) : <SID>PreviousItem(v:count1)<CR>
5553 nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>PreviousItem(v:count1)<CR>
5555 nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>NextItem(v:count1)<CR>
5557 call s:MapMotion('(', 'exe <SID>PreviousItem(v:count1)')
5558 call s:MapMotion(')', 'exe <SID>NextItem(v:count1)')
5559 call s:MapMotion('K', 'exe <SID>PreviousHunk(v:count1)')
5560 call s:MapMotion('J', 'exe <SID>NextHunk(v:count1)')
5561 call s:MapMotion('[c', 'exe <SID>PreviousHunk(v:count1)')
5562 call s:MapMotion(']c', 'exe <SID>NextHunk(v:count1)')
5563 call s:MapMotion('[/', 'exe <SID>PreviousFile(v:count1)')
5564 call s:MapMotion(']/', 'exe <SID>NextFile(v:count1)')
5565 call s:MapMotion('[m', 'exe <SID>PreviousFile(v:count1)')
5566 call s:MapMotion(']m', 'exe <SID>NextFile(v:count1)')
5567 call s:MapMotion('[[', 'exe <SID>PreviousSection(v:count1)')
5568 call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
5569 call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
5570 call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
5571 call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
5572 call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
5574 call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent>')
5575 call s:Map('n', 'dq', ":<C-U>call <SID>DiffClose()<CR>", '<silent>')
5576 call s:Map('n', '-', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>NavigateUp(v:count1))<Bar> if getline(1) =~# '^tree \x\{40,\}$' && empty(getline(2))<Bar>call search('^'.escape(expand('#:t'),'.*[]~\').'/\=$','wc')<Bar>endif<CR>", '<silent>')
5577 call s:Map('n', 'P', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5578 call s:Map('n', '~', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5579 call s:Map('n', 'C', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5580 call s:Map('n', 'cp', ":<C-U>echoerr 'Use gC'<CR>", '<silent>')
5581 call s:Map('n', 'gC', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5582 call s:Map('n', 'gc', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5583 call s:Map('n', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5584 call s:Map('x', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5586 nnoremap <buffer> c<Space> :Git commit<Space>
5587 nnoremap <buffer> c<CR> :Git commit<CR>
5588 nnoremap <buffer> cv<Space> :Git commit -v<Space>
5589 nnoremap <buffer> cv<CR> :Git commit -v<CR>
5590 nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
5591 nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
5592 nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
5593 nnoremap <buffer> <silent> cw :<C-U>Gcommit --amend --only<CR>
5594 nnoremap <buffer> <silent> cva :<C-U>Gcommit -v --amend<CR>
5595 nnoremap <buffer> <silent> cvc :<C-U>Gcommit -v<CR>
5596 nnoremap <buffer> <silent> cRa :<C-U>Gcommit --reset-author --amend<CR>
5597 nnoremap <buffer> <silent> cRe :<C-U>Gcommit --reset-author --amend --no-edit<CR>
5598 nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR>
5599 nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5600 nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5601 nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5602 nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5603 nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
5604 nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR>
5606 nnoremap <buffer> cr<Space> :Git revert<Space>
5607 nnoremap <buffer> cr<CR> :Git revert<CR>
5608 nnoremap <buffer> <silent> crc :<C-U>Grevert <C-R>=<SID>SquashArgument()<CR><CR>
5609 nnoremap <buffer> <silent> crn :<C-U>Grevert --no-commit <C-R>=<SID>SquashArgument()<CR><CR>
5610 nnoremap <buffer> <silent> cr? :help fugitive_cr<CR>
5612 nnoremap <buffer> cm<Space> :Git merge<Space>
5613 nnoremap <buffer> cm<CR> :Git merge<CR>
5614 nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
5616 nnoremap <buffer> cz<Space> :Git stash<Space>
5617 nnoremap <buffer> cz<CR> :Git stash<CR>
5618 nnoremap <buffer> <silent> cza :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5619 nnoremap <buffer> <silent> czA :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', 'stash@{' . v:count . '}'])<CR>
5620 nnoremap <buffer> <silent> czp :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5621 nnoremap <buffer> <silent> czP :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', 'stash@{' . v:count . '}'])<CR>
5622 nnoremap <buffer> <silent> czv :<C-U>exe 'Gedit' fugitive#RevParse('stash@{' . v:count . '}')<CR>
5623 nnoremap <buffer> <silent> czw :<C-U>exe <SID>EchoExec(['stash', '--keep-index'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5624 nnoremap <buffer> <silent> czz :<C-U>exe <SID>EchoExec(['stash'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5625 nnoremap <buffer> <silent> cz? :<C-U>help fugitive_cz<CR>
5627 nnoremap <buffer> co<Space> :Git checkout<Space>
5628 nnoremap <buffer> co<CR> :Git checkout<CR>
5629 nnoremap <buffer> coo :exe <SID>EchoExec(['checkout'] + split(<SID>SquashArgument()) + ['--'])<CR>
5630 nnoremap <buffer> co? :<C-U>help fugitive_co<CR>
5632 nnoremap <buffer> cb<Space> :Git branch<Space>
5633 nnoremap <buffer> cb<CR> :Git branch<CR>
5634 nnoremap <buffer> cb? :<C-U>help fugitive_cb<CR>
5636 nnoremap <buffer> r<Space> :Git rebase<Space>
5637 nnoremap <buffer> r<CR> :Git rebase<CR>
5638 nnoremap <buffer> <silent> ri :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><CR>
5639 nnoremap <buffer> <silent> rf :<C-U>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><CR>
5640 nnoremap <buffer> <silent> ru :<C-U>Grebase --interactive @{upstream}<CR>
5641 nnoremap <buffer> <silent> rp :<C-U>Grebase --interactive @{push}<CR>
5642 nnoremap <buffer> <silent> rw :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/reword/e<CR>
5643 nnoremap <buffer> <silent> rm :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/edit/e<CR>
5644 nnoremap <buffer> <silent> rd :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5645 nnoremap <buffer> <silent> rk :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5646 nnoremap <buffer> <silent> rx :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5647 nnoremap <buffer> <silent> rr :<C-U>Grebase --continue<CR>
5648 nnoremap <buffer> <silent> rs :<C-U>Grebase --skip<CR>
5649 nnoremap <buffer> <silent> re :<C-U>Grebase --edit-todo<CR>
5650 nnoremap <buffer> <silent> ra :<C-U>Grebase --abort<CR>
5651 nnoremap <buffer> <silent> r? :<C-U>help fugitive_r<CR>
5653 call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5654 call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5655 call s:Map('n', 'g?', ":<C-U>help fugitive-map<CR>", '<silent>')
5656 call s:Map('n', '<F1>', ":<C-U>help fugitive-map<CR>", '<silent>')
5660 function! s:StatusCfile(...) abort
5662 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5663 let info = s:StageInfo()
5664 let line = getline('.')
5665 if len(info.sigil) && len(info.section) && len(info.paths)
5666 if info.section ==# 'Unstaged' && info.sigil !=# '-'
5667 return [lead . info.relative[0], info.offset, 'normal!zv']
5668 elseif info.section ==# 'Staged' && info.sigil ==# '-'
5669 return ['@:' . info.relative[0], info.offset, 'normal!zv']
5671 return [':0:' . info.relative[0], info.offset, 'normal!zv']
5673 elseif len(info.paths)
5674 return [lead . info.relative[0]]
5675 elseif len(info.commit)
5676 return [info.commit]
5677 elseif line =~# '^\%(Head\|Merge\|Rebase\|Upstream\|Pull\|Push\): '
5678 return [matchstr(line, ' \zs.*')]
5684 function! fugitive#StatusCfile() abort
5685 let file = s:Generate(s:StatusCfile()[0])
5686 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5689 function! s:MessageCfile(...) abort
5691 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5692 if getline('.') =~# '^.\=\trenamed:.* -> '
5693 return lead . matchstr(getline('.'),' -> \zs.*')
5694 elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
5695 return lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
5696 elseif getline('.') =~# '^.\=\t.'
5697 return lead . matchstr(getline('.'),'\t\zs.*')
5698 elseif getline('.') =~# ': needs merge$'
5699 return lead . matchstr(getline('.'),'.*\ze: needs merge$')
5700 elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
5702 elseif getline('.') =~# '^\%(. \)\=On branch '
5703 return 'refs/heads/'.getline('.')[12:]
5704 elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
5705 return matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
5711 function! fugitive#MessageCfile() abort
5712 let file = s:Generate(s:MessageCfile())
5713 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5716 function! s:cfile() abort
5718 let myhash = s:DirRev(@%)[1]
5721 let myhash = fugitive#RevParse(myhash)
5726 if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w'
5727 let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
5730 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
5732 let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
5733 \ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
5735 if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40,\}\t'
5736 return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
5738 return [treebase . s:sub(getline('.'),'/$','')]
5745 if getline('.') =~# '^\d\{6\} \x\{40,\} \d\t'
5746 let ref = matchstr(getline('.'),'\x\{40,\}')
5747 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
5751 if getline('.') =~# '^ref: '
5752 let ref = strpart(getline('.'),5)
5754 elseif getline('.') =~# '^commit \x\{40,\}\>'
5755 let ref = matchstr(getline('.'),'\x\{40,\}')
5758 elseif getline('.') =~# '^parent \x\{40,\}\>'
5759 let ref = matchstr(getline('.'),'\x\{40,\}')
5760 let line = line('.')
5762 while getline(line) =~# '^parent '
5768 elseif getline('.') =~# '^tree \x\{40,\}$'
5769 let ref = matchstr(getline('.'),'\x\{40,\}')
5770 if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
5771 let ref = myhash.':'
5775 elseif getline('.') =~# '^object \x\{40,\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
5776 let ref = matchstr(getline('.'),'\x\{40,\}')
5777 let type = matchstr(getline(line('.')+1),'type \zs.*')
5779 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
5780 let ref = s:DirRev(@%)[1]
5782 elseif getline('.') =~# '^\l\{3,8\} \x\{40,\}\>'
5783 let ref = matchstr(getline('.'),'\x\{40,\}')
5784 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
5786 elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
5787 let ref = getline('.')[4:]
5789 elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
5790 let type = getline('.')[0]
5791 let lnum = line('.') - 1
5793 while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5794 if getline(lnum) =~# '^[ '.type.']'
5799 let offset += matchstr(getline(lnum), type.'\zs\d\+')
5800 let ref = getline(search('^'.type.'\{3\} [abciow12]/','bnW'))[4:-1]
5801 let dcmds = [offset, 'normal!zv']
5803 elseif getline('.') =~# '^rename from '
5804 let ref = 'a/'.getline('.')[12:]
5805 elseif getline('.') =~# '^rename to '
5806 let ref = 'b/'.getline('.')[10:]
5808 elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5809 let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
5810 let offset = matchstr(getline('.'), '+\zs\d\+')
5812 let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5813 let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5814 let dcmd = 'Gdiffsplit! +'.offset
5816 elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5817 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5818 let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5819 let dcmd = 'Gdiffsplit!'
5821 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5822 let line = getline(line('.')-1)
5823 let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5824 let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5825 let dcmd = 'Gdiffsplit!'
5827 elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
5828 let ref = getline('.')
5830 elseif expand('<cword>') =~# '^\x\{7,\}\>'
5831 return [expand('<cword>')]
5846 let prefixes.a = myhash.'^:'
5847 let prefixes.b = myhash.':'
5849 let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5851 let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5854 if ref ==# '/dev/null'
5856 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
5860 return [ref, dcmd . ' ' . s:fnameescape(dref)] + dcmds
5862 return [ref] + dcmds
5870 function! s:GF(mode) abort
5872 let results = &filetype ==# 'fugitive' ? s:StatusCfile() : &filetype ==# 'gitcommit' ? [s:MessageCfile()] : s:cfile()
5874 return 'echoerr ' . string(v:exception)
5877 return 'G' . a:mode .
5878 \ ' +' . escape(results[1], ' ') . ' ' .
5879 \ s:fnameescape(results[0]) . join(map(results[2:-1], '"|" . v:val'), '')
5880 elseif len(results) && len(results[0])
5881 return 'G' . a:mode . ' ' . s:fnameescape(results[0])
5887 function! fugitive#Cfile() abort
5889 let results = s:cfile()
5891 let cfile = expand('<cfile>')
5892 if &includeexpr =~# '\<v:fname\>'
5893 sandbox let cfile = eval(substitute(&includeexpr, '\C\<v:fname\>', '\=string(cfile)', 'g'))
5896 elseif len(results) > 1
5897 let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' '
5899 return pre . s:fnameescape(s:Generate(results[0]))
5902 " Section: Statusline
5904 function! fugitive#Statusline(...) abort
5905 let dir = s:Dir(bufnr(''))
5910 let commit = s:DirCommitFile(@%)[1]
5912 let status .= ':' . commit[0:6]
5914 let status .= '('.FugitiveHead(7, dir).')'
5915 return '[Git'.status.']'
5918 function! fugitive#statusline(...) abort
5919 return fugitive#Statusline()
5922 function! fugitive#head(...) abort
5927 return fugitive#Head(a:0 ? a:1 : 0)
5932 function! fugitive#Foldtext() abort
5933 if &foldmethod !=# 'syntax'
5937 let line_foldstart = getline(v:foldstart)
5938 if line_foldstart =~# '^diff '
5939 let [add, remove] = [-1, -1]
5941 for lnum in range(v:foldstart, v:foldend)
5942 let line = getline(lnum)
5943 if filename ==# '' && line =~# '^[+-]\{3\} [abciow12]/'
5944 let filename = line[6:-1]
5948 elseif line =~# '^-'
5950 elseif line =~# '^Binary '
5955 let filename = matchstr(line_foldstart, '^diff .\{-\} [abciow12]/\zs.*\ze [abciow12]/')
5958 let filename = line_foldstart[5:-1]
5961 return 'Binary: '.filename
5963 return (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
5965 elseif line_foldstart =~# '^# .*:$'
5966 let lines = getline(v:foldstart, v:foldend)
5967 call filter(lines, 'v:val =~# "^#\t"')
5968 cal map(lines, "s:sub(v:val, '^#\t%(modified: +|renamed: +)=', '')")
5969 cal map(lines, "s:sub(v:val, '^([[:alpha:] ]+): +(.*)', '\\2 (\\1)')")
5970 return line_foldstart.' '.join(lines, ', ')
5975 function! fugitive#foldtext() abort
5976 return fugitive#Foldtext()
5979 augroup fugitive_folding
5981 autocmd User Fugitive
5982 \ if &filetype =~# '^git\%(commit\)\=$' && &foldtext ==# 'foldtext()' |
5983 \ set foldtext=fugitive#Foldtext() |
5987 " Section: Initialization
5989 function! fugitive#Init() abort
5990 if exists('#User#FugitiveBoot')
5991 exe s:DoAutocmd('User FugitiveBoot')
5994 if &tags !~# '\.git' && @% !~# '\.git' && !exists('s:tags_warning')
5995 let actualdir = fugitive#Find('.git/', dir)
5996 if filereadable(actualdir . 'tags')
5997 let s:tags_warning = 1
5999 echo "Fugitive .git/tags support removed in favor of `:set tags^=./.git/tags;`"
6003 exe s:DoAutocmd('User Fugitive')
6006 function! fugitive#is_git_dir(path) abort
6007 return FugitiveIsGitDir(a:path)
6010 function! fugitive#extract_git_dir(path) abort
6011 return FugitiveExtractGitDir(a:path)
6014 function! fugitive#detect(path) abort
6015 return FugitiveDetect(a:path)