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]
1620 let cmd += ['status', '--porcelain', '-bz']
1621 let [output, message, exec_error] = s:NullError(cmd)
1623 throw 'fugitive: ' . message
1626 let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
1628 if head =~# '\.\.\.'
1629 let [head, pull] = split(head, '\.\.\.')
1631 elseif head ==# 'HEAD' || empty(head)
1632 let head = FugitiveHead(11)
1638 let b:fugitive_files = {'Staged': {}, 'Unstaged': {}}
1639 let [staged, unstaged, untracked] = [[], [], []]
1641 while i < len(output)
1642 let line = output[i]
1643 let file = line[3:-1]
1649 if line[0:1] =~# '[RC]'
1650 let files = output[i] . ' -> ' . file
1653 if line[0] !~# '[ ?!#]'
1654 call add(staged, {'type': 'File', 'status': line[0], 'filename': files})
1656 if line[0:1] ==# '??'
1657 call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
1658 elseif line[1] !~# '[ !#]'
1659 call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files})
1664 let b:fugitive_files['Staged'][dict.filename] = dict
1666 for dict in unstaged
1667 let b:fugitive_files['Unstaged'][dict.filename] = dict
1670 let config = fugitive#Config()
1672 let pull_type = 'Pull'
1674 let rebase = fugitive#Config('branch.' . branch . '.rebase', config)
1676 let rebase = fugitive#Config('pull.rebase', config)
1678 if rebase =~# '^\%(true\|yes\|on\|1\|interactive\)$'
1679 let pull_type = 'Rebase'
1680 elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
1681 let pull_type = 'Merge'
1685 let push_remote = fugitive#Config('branch.' . branch . '.pushRemote', config)
1686 if empty(push_remote)
1687 let push_remote = fugitive#Config('remote.pushDefault', config)
1689 let push = len(push_remote) && len(branch) ? push_remote . '/' . branch : ''
1695 let unpulled = s:QueryLog(head . '..' . pull)
1700 let unpushed = s:QueryLog(push . '..' . head)
1705 if isdirectory(fugitive#Find('.git/rebase-merge/'))
1706 let rebasing_dir = fugitive#Find('.git/rebase-merge/')
1707 elseif isdirectory(fugitive#Find('.git/rebase-apply/'))
1708 let rebasing_dir = fugitive#Find('.git/rebase-apply/')
1712 let rebasing_head = 'detached HEAD'
1713 if exists('rebasing_dir') && filereadable(rebasing_dir . 'git-rebase-todo')
1714 let rebasing_head = substitute(readfile(rebasing_dir . 'head-name')[0], '\C^refs/heads/', '', '')
1716 let lines = readfile(rebasing_dir . 'git-rebase-todo')
1718 let hash = matchstr(line, '^[^a-z].*\s\zs[0-9a-f]\{4,\}\ze\.\.')
1724 if getfsize(rebasing_dir . 'done') > 0
1725 let done = readfile(rebasing_dir . 'done')
1726 call map(done, 'substitute(v:val, ''^\l\+\>'', "done", "")')
1727 let done[-1] = substitute(done[-1], '^\l\+\>', 'stop', '')
1728 let lines = done + lines
1732 let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
1733 if len(match) && match[1] !~# 'exec\|merge\|label'
1734 call add(rebasing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': strpart(match[2], 0, len), 'subject': match[3]})
1739 let diff = {'Staged': [], 'Unstaged': []}
1741 let diff['Staged'] =
1742 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached'])[0]
1745 let diff['Unstaged'] =
1746 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix'])[0]
1748 let b:fugitive_diff = diff
1749 let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}})
1750 let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}}
1752 silent keepjumps %delete_
1754 call s:AddHeader('Head', head)
1755 call s:AddHeader(pull_type, pull)
1757 call s:AddHeader('Push', push)
1759 call s:AddSection('Rebasing ' . rebasing_head, rebasing)
1760 call s:AddSection('Untracked', untracked)
1761 call s:AddSection('Unstaged', unstaged)
1762 let unstaged_end = len(unstaged) ? line('$') : 0
1763 call s:AddSection('Staged', staged)
1764 let staged_end = len(staged) ? line('$') : 0
1765 call s:AddSection('Unpushed to ' . push, unpushed)
1766 call s:AddSection('Unpulled from ' . pull, unpulled)
1768 setlocal nomodified readonly noswapfile
1769 silent doautocmd BufReadPost
1770 setlocal nomodifiable
1771 if &bufhidden ==# ''
1772 setlocal bufhidden=delete
1774 let b:dispatch = ':Gfetch --all'
1775 call fugitive#MapJumps()
1776 call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1777 call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
1778 call s:Map('n', 's', ":<C-U>execute <SID>Do('Stage',0)<CR>", '<silent>')
1779 call s:Map('x', 's', ":<C-U>execute <SID>Do('Stage',1)<CR>", '<silent>')
1780 call s:Map('n', 'u', ":<C-U>execute <SID>Do('Unstage',0)<CR>", '<silent>')
1781 call s:Map('x', 'u', ":<C-U>execute <SID>Do('Unstage',1)<CR>", '<silent>')
1782 call s:Map('n', 'U', ":exe <SID>EchoExec('reset', '-q')<CR>", '<silent>')
1783 call s:MapMotion('gu', "exe <SID>StageJump(v:count, 'Untracked', 'Unstaged')")
1784 call s:MapMotion('gU', "exe <SID>StageJump(v:count, 'Unstaged', 'Untracked')")
1785 call s:MapMotion('gs', "exe <SID>StageJump(v:count, 'Staged')")
1786 call s:MapMotion('gp', "exe <SID>StageJump(v:count, 'Unpushed')")
1787 call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
1788 call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
1789 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>')
1790 call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1791 call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
1792 call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
1793 call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
1794 call s:Map('n', ">", ":<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>", '<silent>')
1795 call s:Map('x', "=", ":<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1796 call s:Map('x', "<", ":<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1797 call s:Map('x', ">", ":<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1798 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>')
1799 call s:Map('n', 'dd', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>", '<silent>')
1800 call s:Map('n', 'dh', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1801 call s:Map('n', 'ds', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1802 call s:Map('n', 'dp', ":<C-U>execute <SID>StageDiffEdit()<CR>", '<silent>')
1803 call s:Map('n', 'dv', ":<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>", '<silent>')
1804 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
1805 call s:Map('n', 'P', ":<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>", '<silent>')
1806 call s:Map('x', 'P', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1807 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>')
1808 call s:Map('x', 'p', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1809 call s:Map('n', 'I', ":<C-U>execute <SID>StagePatch(line('.'),line('.'))<CR>", '<silent>')
1810 call s:Map('x', 'I', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1811 if empty(mapcheck('q', 'n'))
1812 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>
1814 call s:Map('n', 'gq', ":<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>", '<silent>')
1815 call s:Map('n', 'R', ":echohl WarningMsg<Bar>echo 'Reloading is automatic. Use :e to force'<Bar>echohl NONE<CR>", '<silent>')
1816 call s:Map('n', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1817 call s:Map('x', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1818 call s:Map('n', 'X', ":<C-U>execute <SID>StageDelete(line('.'), 0, v:count)<CR>", '<silent>')
1819 call s:Map('x', 'X', ":<C-U>execute <SID>StageDelete(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1820 call s:Map('n', 'gI', ":<C-U>execute <SID>StageIgnore(line('.'), line('.'), v:count)<CR>", '<silent>')
1821 call s:Map('x', 'gI', ":<C-U>execute <SID>StageIgnore(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1822 call s:Map('n', '.', ':<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>')
1823 call s:Map('x', '.', ':<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>')
1824 setlocal filetype=fugitive
1826 for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']]
1827 while len(getline(lnum))
1828 let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*')
1829 if has_key(expanded[section], filename)
1830 call s:StageInline('show', lnum)
1836 let b:fugitive_reltime = reltime()
1839 return 'echoerr ' . string(v:exception)
1843 function! fugitive#FileReadCmd(...) abort
1844 let amatch = a:0 ? a:1 : expand('<amatch>')
1845 let [dir, rev] = s:DirRev(amatch)
1846 let line = a:0 > 1 ? a:2 : line("'[")
1848 return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
1850 if rev !~# ':' && s:ChompDefault('', [dir, 'cat-file', '-t', rev]) =~# '^\%(commit\|tag\)$'
1851 let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev, '--')
1853 let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev)
1855 return line . 'read !' . escape(cmd, '!#%')
1858 function! fugitive#FileWriteCmd(...) abort
1859 let tmp = tempname()
1860 let amatch = a:0 ? a:1 : expand('<amatch>')
1861 let autype = a:0 > 1 ? 'Buf' : 'File'
1862 if exists('#' . autype . 'WritePre')
1863 execute s:DoAutocmd(autype . 'WritePre ' . s:fnameescape(amatch))
1866 let [dir, commit, file] = s:DirCommitFile(amatch)
1867 if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$'))
1868 return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch)
1870 silent execute "'[,']write !".fugitive#Prepare(dir, 'hash-object', '-w', '--stdin', '--').' > '.tmp
1871 let sha1 = readfile(tmp)[0]
1872 let old_mode = matchstr(s:SystemError([dir, 'ls-files', '--stage', '.' . file])[0], '^\d\+')
1874 let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
1876 let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1]
1877 let [error, exec_error] = s:SystemError([dir, 'update-index', '--index-info'], info . "\n")
1880 if exists('#' . autype . 'WritePost')
1881 execute s:DoAutocmd(autype . 'WritePost ' . s:fnameescape(amatch))
1885 return 'echoerr '.string('fugitive: '.error)
1892 function! fugitive#BufReadCmd(...) abort
1893 let amatch = a:0 ? a:1 : expand('<amatch>')
1895 let [dir, rev] = s:DirRev(amatch)
1897 return 'echo "Invalid Fugitive URL"'
1900 let b:fugitive_type = 'stage'
1902 let [b:fugitive_type, exec_error] = s:ChompError([dir, 'cat-file', '-t', rev])
1903 if exec_error && rev =~# '^:0'
1904 let sha = s:ChompDefault('', dir, 'write-tree', '--prefix=' . rev[3:-1])
1905 let exec_error = empty(sha)
1906 let b:fugitive_type = exec_error ? '' : 'tree'
1909 let error = b:fugitive_type
1910 unlet b:fugitive_type
1912 if empty(&bufhidden)
1913 setlocal bufhidden=delete
1916 let &l:readonly = !filewritable(fugitive#Find('.git/index', dir))
1917 return 'silent doautocmd BufNewFile'
1919 setlocal readonly nomodifiable
1920 return 'silent doautocmd BufNewFile|echo ' . string(error)
1922 elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
1923 return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'")
1925 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
1926 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
1930 if b:fugitive_type !=# 'blob'
1934 setlocal noreadonly modifiable
1935 let pos = getpos('.')
1936 silent keepjumps %delete_
1940 silent doautocmd BufReadPre
1941 if b:fugitive_type ==# 'tree'
1942 let b:fugitive_display_format = b:fugitive_display_format % 2
1943 if b:fugitive_display_format
1944 call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev])
1947 let sha = s:TreeChomp(dir, 'rev-parse', '--verify', rev, '--')
1949 call s:ReplaceCmd([dir, 'show', '--no-color', sha])
1951 elseif b:fugitive_type ==# 'tag'
1952 let b:fugitive_display_format = b:fugitive_display_format % 2
1953 if b:fugitive_display_format
1954 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1956 call s:ReplaceCmd([dir, 'cat-file', '-p', rev])
1958 elseif b:fugitive_type ==# 'commit'
1959 let b:fugitive_display_format = b:fugitive_display_format % 2
1960 if b:fugitive_display_format
1961 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1963 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])
1964 keepjumps call search('^parent ')
1965 if getline('.') ==# 'parent '
1966 silent keepjumps delete_
1968 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
1970 keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
1972 silent keepjumps delete_
1974 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
1977 elseif b:fugitive_type ==# 'stage'
1978 call s:ReplaceCmd([dir, 'ls-files', '--stage'])
1979 elseif b:fugitive_type ==# 'blob'
1980 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1983 keepjumps call setpos('.',pos)
1984 setlocal nomodified noswapfile
1985 let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
1986 let &l:readonly = !modifiable || !filewritable(fugitive#Find('.git/index', dir))
1987 if empty(&bufhidden)
1988 setlocal bufhidden=delete
1990 let &l:modifiable = modifiable
1991 if b:fugitive_type !=# 'blob'
1992 setlocal filetype=git foldmethod=syntax
1993 call s:Map('n', 'a', ":<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
1994 call s:Map('n', 'i', ":<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
1996 call fugitive#MapJumps()
2000 return 'silent ' . s:DoAutocmd('BufReadPost') .
2001 \ (modifiable ? '' : '|setl nomodifiable')
2003 return 'echoerr ' . string(v:exception)
2007 function! fugitive#BufWriteCmd(...) abort
2008 return fugitive#FileWriteCmd(a:0 ? a:1 : expand('<amatch>'), 1)
2011 function! fugitive#SourceCmd(...) abort
2012 let amatch = a:0 ? a:1 : expand('<amatch>')
2013 let temp = s:BlobTemp(amatch)
2015 return 'noautocmd source ' . s:fnameescape(amatch)
2017 if !exists('g:virtual_scriptnames')
2018 let g:virtual_scriptnames = {}
2020 let g:virtual_scriptnames[temp] = amatch
2021 return 'source ' . s:fnameescape(temp)
2024 " Section: Temp files
2026 if !exists('s:temp_files')
2027 let s:temp_files = {}
2030 function! s:TempState(...) abort
2031 return get(s:temp_files, s:cpath(fnamemodify(a:0 ? a:1 : @%, ':p')), {})
2034 function! s:TempReadPre(file) abort
2035 if has_key(s:temp_files, s:cpath(a:file))
2036 let dict = s:temp_files[s:cpath(a:file)]
2038 setlocal bufhidden=delete nobuflisted
2039 setlocal buftype=nowrite
2040 if has_key(dict, 'modifiable')
2041 let &l:modifiable = dict.modifiable
2044 let b:git_dir = dict.dir
2045 call extend(b:, {'fugitive_type': 'temp'}, 'keep')
2050 function! s:TempReadPost(file) abort
2051 if has_key(s:temp_files, s:cpath(a:file))
2052 let dict = s:temp_files[s:cpath(a:file)]
2053 if has_key(dict, 'filetype') && dict.filetype !=# &l:filetype
2054 let &l:filetype = dict.filetype
2056 setlocal foldmarker=<<<<<<<,>>>>>>>
2057 if empty(mapcheck('q', 'n'))
2058 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>
2061 call s:Map('n', 'gq', ":<C-U>bdelete<CR>", '<silent> <unique>')
2067 augroup fugitive_temp
2069 autocmd BufReadPre * exe s:TempReadPre( expand('<amatch>:p'))
2070 autocmd BufReadPost * exe s:TempReadPost(expand('<amatch>:p'))
2075 function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
2077 let [args, after] = s:SplitExpandChain(a:arg, s:Tree(dir))
2079 let cmd = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
2080 return (empty(cmd) ? 'exe' : cmd) . after
2082 let alias = get(s:Aliases(dir), args[0], '!')
2083 if get(args, 1, '') !=# '--help' && alias !~# '^!\|[\"'']' && !filereadable(s:ExecPath() . '/git-' . args[0])
2084 \ && !(has('win32') && filereadable(s:ExecPath() . '/git-' . args[0] . '.exe'))
2085 call remove(args, 0)
2086 call extend(args, split(alias, '\s\+'), 'keep')
2088 let name = substitute(args[0], '\%(^\|-\)\(\l\)', '\u\1', 'g')
2089 if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
2092 return 'exe ' . string(s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])) . after
2094 return 'echoerr ' . string(v:exception)
2097 if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' ||
2098 \ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
2099 \ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
2100 return s:OpenExec((a:line2 > 0 ? a:line2 : '') . (a:line2 ? 'split' : 'edit'), a:mods, args, dir) . after
2102 if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') ||
2103 \ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive') ||
2104 \ index(['--paginate', '-p'], args[0]) >= 0
2105 let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
2106 let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : ''
2108 if &autowrite || &autowriteall | silent! wall | endif
2109 return mods . (a:line2 ? 'split' : 'edit') . ' term://' . s:fnameescape(s:UserCommand(dir, args)) . assign . '|startinsert' . after
2110 elseif has('terminal')
2111 if &autowrite || &autowriteall | silent! wall | endif
2112 return 'exe ' . string(mods . 'terminal ' . (a:line2 ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . assign . after
2115 if has('gui_running') && !has('win32')
2116 call insert(args, '--no-pager')
2119 if has('nvim') && executable('env')
2120 let pre .= 'env GIT_TERMINAL_PROMPT=0 '
2122 return 'exe ' . string('noautocmd !' . escape(pre . s:UserCommand(dir, args), '!#%')) .
2123 \ '|call fugitive#ReloadStatus(' . string(dir) . ', 1)' .
2127 let s:exec_paths = {}
2128 function! s:ExecPath() abort
2129 if !has_key(s:exec_paths, g:fugitive_git_executable)
2130 let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
2132 return s:exec_paths[g:fugitive_git_executable]
2135 function! s:Subcommands() abort
2136 let exec_path = s:ExecPath()
2137 return map(split(glob(exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(exec_path)+5 : -1],"\\.exe$","")')
2141 function! s:Aliases(dir) abort
2142 if !has_key(s:aliases, a:dir)
2143 let s:aliases[a:dir] = {}
2144 let lines = s:NullError([a:dir, 'config', '-z', '--get-regexp', '^alias[.]'])[0]
2146 let s:aliases[a:dir][matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*')
2149 return s:aliases[a:dir]
2152 function! fugitive#Complete(lead, ...) abort
2153 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
2154 let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
2155 let subcmd = matchstr(pre, '\u\w*[! ] *\zs[[:alnum:]-]\+\ze ')
2157 let results = sort(s:Subcommands() + keys(s:Aliases(dir)))
2158 elseif pre =~# ' -- '
2159 return fugitive#CompletePath(a:lead, dir)
2160 elseif a:lead =~# '^-'
2161 let results = split(s:ChompDefault('', dir, subcmd, '--git-completion-helper'), ' ')
2163 return fugitive#CompleteObject(a:lead, dir)
2165 return filter(results, 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
2168 " Section: :Gcd, :Glcd
2170 function! fugitive#CdComplete(A, L, P) abort
2171 return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
2174 function! fugitive#Cd(path, ...) abort
2175 let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
2176 if path !~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
2179 let path = (empty(s:Tree(dir)) ? dir : s:Tree(dir)) . '/' . path
2181 return (a:0 && a:1 ? 'lcd ' : 'cd ') . s:fnameescape(FugitiveVimPath(path))
2186 function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
2187 let dir = a:0 ? a:1 : s:Dir()
2190 let mods = s:Mods(a:mods, &splitbelow ? 'botright' : 'topleft')
2191 let file = fugitive#Find(':', dir)
2192 let arg = ' +setl\ foldmethod=syntax\ foldlevel=1\|let\ w:fugitive_status=FugitiveGitDir() ' .
2193 \ s:fnameescape(file)
2194 for winnr in range(1, winnr('$'))
2195 if s:cpath(file, fnamemodify(bufname(winbufnr(winnr)), ':p'))
2197 call s:ReloadStatus()
2199 call s:ExpireStatus(dir)
2200 exe winnr . 'wincmd w'
2202 let w:fugitive_status = dir
2208 return mods . 'edit' . (a:bang ? '!' : '') . arg
2210 return mods . 'pedit' . arg . '|wincmd P'
2212 return mods . (a:count > 0 ? a:count : '') . 'split' . arg
2215 return 'echoerr ' . string(v:exception)
2220 function! s:StageJump(offset, section, ...) abort
2221 let line = search('^\%(' . a:section . '\)', 'nw')
2223 let line = search('^\%(' . a:1 . '\)', 'nw')
2228 for i in range(a:offset)
2229 call search(s:file_commit_pattern . '\|^$', 'W')
2230 if empty(getline('.')) && a:0 && getline(line('.') + 1) =~# '^\%(' . a:1 . '\)'
2231 call search(s:file_commit_pattern . '\|^$', 'W')
2233 if empty(getline('.'))
2237 call s:StageReveal()
2239 call s:StageReveal()
2246 function! s:StageSeek(info, fallback) abort
2248 if empty(info.section)
2251 let line = search('^' . info.section, 'wn')
2253 for section in get({'Staged': ['Unstaged', 'Untracked'], 'Unstaged': ['Untracked', 'Staged'], 'Untracked': ['Unstaged', 'Staged']}, info.section, [])
2254 let line = search('^' . section, 'wn')
2256 return line + (info.index > 0 ? 1 : 0)
2262 while len(getline(line))
2263 let filename = matchstr(getline(line), '^[A-Z?] \zs.*')
2265 \ ((info.filename[-1:-1] ==# '/' && filename[0 : len(info.filename) - 1] ==# info.filename) ||
2266 \ (filename[-1:-1] ==# '/' && filename ==# info.filename[0 : len(filename) - 1]) ||
2267 \ filename ==# info.filename)
2271 if getline(line+1) !~# '^@'
2272 exe s:StageInline('show', line)
2274 if getline(line+1) !~# '^@'
2277 let type = info.sigil ==# '-' ? '-' : '+'
2279 while offset < info.offset
2281 if getline(line) =~# '^@'
2282 let offset = +matchstr(getline(line), type . '\zs\d\+') - 1
2283 elseif getline(line) =~# '^[ ' . type . ']'
2285 elseif getline(line) !~# '^[ @\+-]'
2292 let commit = matchstr(getline(line), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\+')
2293 if len(commit) && commit ==# info.commit
2299 let i += getline(line) !~# '^[ @\+-]'
2302 return exists('backup') ? backup : line - 1
2305 function! s:DoAutocmdChanged(dir) abort
2306 let dir = a:dir is# -2 ? '' : FugitiveGitDir(a:dir)
2307 if empty(dir) || !exists('#User#FugitiveChanged') || exists('g:fugitive_event')
2311 let g:fugitive_event = dir
2312 exe s:DoAutocmd('User FugitiveChanged')
2314 unlet! g:fugitive_event
2315 " Force statusline reload with the buffer's Git dir
2321 function! s:ReloadStatusBuffer(...) abort
2322 if get(b:, 'fugitive_type', '') !=# 'index'
2325 let original_lnum = a:0 ? a:1 : line('.')
2326 let info = s:StageInfo(original_lnum)
2327 call fugitive#BufReadStatus()
2328 exe s:StageSeek(info, original_lnum)
2333 function! s:ReloadStatus(...) abort
2334 call s:ExpireStatus(-1)
2335 call s:ReloadStatusBuffer(a:0 ? a:1 : line('.'))
2336 exe s:DoAutocmdChanged(-1)
2340 let s:last_time = reltime()
2341 if !exists('s:last_times')
2342 let s:last_times = {}
2345 function! s:ExpireStatus(bufnr) abort
2347 let s:last_time = reltime()
2350 let dir = s:Dir(a:bufnr)
2352 let s:last_times[s:cpath(dir)] = reltime()
2357 function! FugitiveReloadCheck() abort
2358 let t = b:fugitive_reltime
2359 return [t, reltimestr(reltime(s:last_time, t)),
2360 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t))]
2363 function! s:ReloadWinStatus(...) abort
2364 if get(b:, 'fugitive_type', '') !=# 'index' || &modified
2367 if !exists('b:fugitive_reltime')
2368 exe s:ReloadStatusBuffer()
2371 let t = b:fugitive_reltime
2372 if reltimestr(reltime(s:last_time, t)) =~# '-\|\d\{10\}\.' ||
2373 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t)) =~# '-\|\d\{10\}\.'
2374 exe s:ReloadStatusBuffer()
2378 function! s:ReloadTabStatus(...) abort
2379 let mytab = tabpagenr()
2380 let tab = a:0 ? a:1 : mytab
2381 for winnr in range(1, tabpagewinnr(tab, '$'))
2382 if getbufvar(tabpagebuflist(tab)[winnr-1], 'fugitive_type') ==# 'index'
2383 execute 'tabnext '.tab
2385 execute winnr.'wincmd w'
2386 let restorewinnr = 1
2389 call s:ReloadWinStatus()
2391 if exists('restorewinnr')
2395 execute 'tabnext '.mytab
2399 unlet! t:fugitive_reload_status
2402 function! fugitive#ReloadStatus(...) abort
2403 call s:ExpireStatus(a:0 ? a:1 : -1)
2404 if a:0 > 1 ? a:2 : 1
2406 let t:fugitive_reload_status = t
2407 for tabnr in exists('*settabvar') ? range(1, tabpagenr('$')) : []
2408 call settabvar(tabnr, 'fugitive_reload_status', t)
2410 call s:ReloadTabStatus()
2411 exe s:DoAutocmdChanged(a:0 ? a:1 : -1)
2413 call s:ReloadWinStatus()
2418 function! fugitive#EfmDir(...) abort
2419 let dir = matchstr(a:0 ? a:1 : &errorformat, '\c,%\\&\%(git\|fugitive\)_\=dir=\zs\%(\\.\|[^,]\)*')
2420 let dir = substitute(dir, '%%', '%', 'g')
2421 let dir = substitute(dir, '\\\ze[\,]', '', 'g')
2425 augroup fugitive_status
2427 autocmd BufWritePost * call fugitive#ReloadStatus(-1, 0)
2428 autocmd ShellCmdPost,ShellFilterPost * nested call fugitive#ReloadStatus(-2, 0)
2429 autocmd BufDelete * nested
2430 \ if getbufvar(+expand('<abuf>'), 'buftype') ==# 'terminal' |
2431 \ if !empty(FugitiveGitDir(+expand('<abuf>'))) |
2432 \ call fugitive#ReloadStatus(+expand('<abuf>'), 1) |
2434 \ call fugitive#ReloadStatus(-2, 0) |
2437 autocmd QuickFixCmdPost make,lmake,[cl]file,[cl]getfile nested
2438 \ call fugitive#ReloadStatus(fugitive#EfmDir(), 1)
2440 autocmd FocusGained * call fugitive#ReloadStatus(-2, 0)
2442 autocmd BufEnter index,index.lock
2443 \ call s:ReloadWinStatus()
2445 \ if exists('t:fugitive_reload_status') |
2446 \ call s:ReloadTabStatus() |
2450 function! s:StageInfo(...) abort
2451 let lnum = a:0 ? a:1 : line('.')
2452 let sigil = matchstr(getline(lnum), '^[ @\+-]')
2455 let type = sigil ==# '-' ? '-' : '+'
2456 while lnum > 0 && getline(lnum) !~# '^@'
2457 if getline(lnum) =~# '^[ '.type.']'
2462 let offset += matchstr(getline(lnum), type.'\zs\d\+')
2463 while getline(lnum) =~# '^[ @\+-]'
2467 let slnum = lnum + 1
2470 while len(getline(slnum - 1)) && empty(section)
2472 let section = matchstr(getline(slnum), '^\u\l\+\ze.* (\d\+)$')
2473 if empty(section) && getline(slnum) !~# '^[ @\+-]'
2477 let text = matchstr(getline(lnum), '^[A-Z?] \zs.*')
2478 return {'section': section,
2479 \ 'heading': getline(slnum),
2483 \ 'relative': reverse(split(text, ' -> ')),
2484 \ 'paths': map(reverse(split(text, ' -> ')), 's:Tree() . "/" . v:val'),
2485 \ 'commit': matchstr(getline(lnum), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze '),
2486 \ 'status': matchstr(getline(lnum), '^[A-Z?]\ze \|^\%(\x\x\x\)\@!\l\+\ze [0-9a-f]'),
2490 function! s:Selection(arg1, ...) abort
2492 let arg1 = line('.')
2494 elseif a:arg1 ==# 'v'
2495 let arg1 = line("'<")
2496 let arg2 = line("'>")
2499 let arg2 = a:0 ? a:1 : 0
2503 let last = first - arg2 - 1
2509 while getline(first) =~# '^$\|^[A-Z][a-z]'
2512 if first > last || &filetype !=# 'fugitive'
2516 while getline(flnum) =~# '^[ @\+-]'
2519 let slnum = flnum + 1
2522 while len(getline(slnum - 1)) && empty(section)
2524 let heading = matchstr(getline(slnum), '^\u\l\+.* (\d\+)$')
2525 if empty(heading) && getline(slnum) !~# '^[ @\+-]'
2531 \ 'heading': heading,
2532 \ 'section': matchstr(heading, '^\u\l\+\ze.* (\d\+)$'),
2540 let line = getline(flnum)
2541 let lnum = first - (arg1 == flnum ? 0 : 1)
2542 let root = s:Tree() . '/'
2544 if line =~# '^\u\l\+\ze.* (\d\+)$'
2545 let template.heading = getline(lnum)
2546 let template.section = matchstr(template.heading, '^\u\l\+\ze.* (\d\+)$')
2547 let template.index = 0
2548 elseif line =~# '^[ @\+-]'
2549 let template.index -= 1
2550 if !results[-1].patch
2551 let results[-1].patch = lnum
2553 let results[-1].lnum = lnum
2554 elseif line =~# '^[A-Z?] '
2555 let filename = matchstr(line, '^[A-Z?] \zs.*')
2556 call add(results, extend(deepcopy(template), {
2558 \ 'filename': filename,
2559 \ 'relative': reverse(split(filename, ' -> ')),
2560 \ 'paths': map(reverse(split(filename, ' -> ')), 'root . v:val'),
2561 \ 'status': matchstr(line, '^[A-Z?]'),
2563 elseif line =~# '^\x\x\x\+ '
2564 call add(results, extend({
2566 \ 'commit': matchstr(line, '^\x\x\x\+'),
2567 \ }, template, 'keep'))
2568 elseif line =~# '^\l\+ \x\x\x\+ '
2569 call add(results, extend({
2571 \ 'commit': matchstr(line, '^\l\+ \zs\x\x\x\+'),
2572 \ 'status': matchstr(line, '^\l\+'),
2573 \ }, template, 'keep'))
2576 let template.index += 1
2577 let line = getline(lnum)
2579 if len(results) && results[0].patch && arg2 == 0
2580 while getline(results[0].patch) =~# '^[ \+-]'
2581 let results[0].patch -= 1
2583 while getline(results[0].lnum + 1) =~# '^[ \+-]'
2584 let results[0].lnum += 1
2590 function! s:StageArgs(visual) abort
2593 for record in s:Selection(a:visual ? 'v' : 'n')
2594 if len(record.commit)
2595 call add(commits, record.commit)
2597 call extend(paths, record.paths)
2599 if s:cpath(s:Tree(), getcwd())
2600 call map(paths, 'fugitive#Path(v:val, "./")')
2602 return join(map(commits + paths, 's:fnameescape(v:val)'), ' ')
2605 function! s:Do(action, visual) abort
2606 let line = getline('.')
2608 if !a:visual && !v:count && line =~# '^[A-Z][a-z]'
2609 let header = matchstr(line, '^\S\+\ze:')
2610 if len(header) && exists('*s:Do' . a:action . header . 'Header')
2611 let reload = s:Do{a:action}{header}Header(matchstr(line, ': \zs.*')) > 0
2613 let section = matchstr(line, '^\S\+')
2614 if exists('*s:Do' . a:action . section . 'Heading')
2615 let reload = s:Do{a:action}{section}Heading(line) > 0
2618 return reload ? s:ReloadStatus() : ''
2620 let selection = s:Selection(a:visual ? 'v' : 'n')
2624 call filter(selection, 'v:val.section ==# selection[0].section')
2628 for record in selection
2629 if exists('*s:Do' . a:action . record.section)
2630 let status = s:Do{a:action}{record.section}(record)
2637 let reload = reload || (status > 0)
2640 execute record.lnum + 1
2644 return 'echoerr ' . string(v:exception)
2647 execute s:ReloadStatus()
2649 if exists('success')
2650 call s:StageReveal()
2656 function! s:StageReveal() abort
2658 let begin = line('.')
2659 if getline(begin) =~# '^@'
2661 while getline(end) =~# '^[ \+-]'
2664 elseif getline(begin) =~# '^commit '
2666 while end < line('$') && getline(end + 1) !~# '^commit '
2669 elseif getline(begin) =~# s:section_pattern
2671 while len(getline(end + 1))
2676 while line('.') > line('w0') + &scrolloff && end > line('w$')
2677 execute "normal! \<C-E>"
2682 let s:file_pattern = '^[A-Z?] .\|^diff --'
2683 let s:file_commit_pattern = s:file_pattern . '\|^\%(\l\{3,\} \)\=[0-9a-f]\{4,\} '
2684 let s:item_pattern = s:file_commit_pattern . '\|^@@'
2686 function! s:NextHunk(count) abort
2687 if &filetype ==# 'fugitive' && getline('.') =~# s:file_pattern
2688 exe s:StageInline('show')
2690 for i in range(a:count)
2691 if &filetype ==# 'fugitive'
2692 call search(s:file_pattern . '\|^@', 'W')
2693 if getline('.') =~# s:file_pattern
2694 exe s:StageInline('show')
2695 if getline(line('.') + 1) =~# '^@'
2700 call search('^@@', 'W')
2703 call s:StageReveal()
2707 function! s:PreviousHunk(count) abort
2708 for i in range(a:count)
2709 if &filetype ==# 'fugitive'
2710 let lnum = search(s:file_pattern . '\|^@','Wbn')
2711 call s:StageInline('show', lnum)
2712 call search('^? .\|^@','Wb')
2714 call search('^@@', 'Wb')
2717 call s:StageReveal()
2721 function! s:NextFile(count) abort
2722 for i in range(a:count)
2723 exe s:StageInline('hide')
2724 if !search(s:file_pattern, 'W')
2728 exe s:StageInline('hide')
2732 function! s:PreviousFile(count) abort
2733 exe s:StageInline('hide')
2734 for i in range(a:count)
2735 if !search(s:file_pattern, 'Wb')
2738 exe s:StageInline('hide')
2743 function! s:NextItem(count) abort
2744 for i in range(a:count)
2745 if !search(s:item_pattern, 'W') && getline('.') !~# s:item_pattern
2746 call search('^commit ', 'W')
2749 call s:StageReveal()
2753 function! s:PreviousItem(count) abort
2754 for i in range(a:count)
2755 if !search(s:item_pattern, 'Wbe') && getline('.') !~# s:item_pattern
2756 call search('^commit ', 'Wbe')
2759 call s:StageReveal()
2763 let s:section_pattern = '^[A-Z][a-z][^:]*$'
2764 let s:section_commit_pattern = s:section_pattern . '\|^commit '
2766 function! s:NextSection(count) abort
2767 let orig = line('.')
2768 if getline('.') !~# '^commit '
2771 for i in range(a:count)
2772 if !search(s:section_commit_pattern, 'W')
2776 if getline('.') =~# s:section_commit_pattern
2777 call s:StageReveal()
2778 return getline('.') =~# s:section_pattern ? '+' : ':'
2784 function! s:PreviousSection(count) abort
2785 let orig = line('.')
2786 if getline('.') !~# '^commit '
2789 for i in range(a:count)
2790 if !search(s:section_commit_pattern . '\|\%^', 'bW')
2794 if getline('.') =~# s:section_commit_pattern || line('.') == 1
2795 call s:StageReveal()
2796 return getline('.') =~# s:section_pattern ? '+' : ':'
2802 function! s:NextSectionEnd(count) abort
2804 if empty(getline('.'))
2807 for i in range(a:count)
2808 if !search(s:section_commit_pattern, 'W')
2812 return search('^.', 'Wb')
2815 function! s:PreviousSectionEnd(count) abort
2817 for i in range(a:count)
2818 if search(s:section_commit_pattern, 'Wb') <= 1
2828 return search('^.', 'Wb')
2831 function! s:PatchSearchExpr(reverse) abort
2832 let line = getline('.')
2833 if col('.') ==# 1 && line =~# '^[+-]'
2834 if line =~# '^[+-]\{3\} '
2835 let pattern = '^[+-]\{3\} ' . substitute(escape(strpart(line, 4), '^$.*[]~\'), '^\w/', '\\w/', '') . '$'
2837 let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
2840 return '?' . escape(pattern, '/') . "\<CR>"
2842 return '/' . escape(pattern, '/?') . "\<CR>"
2845 return a:reverse ? '#' : '*'
2848 function! s:StageInline(mode, ...) abort
2849 if &filetype !=# 'fugitive'
2852 let lnum1 = a:0 ? a:1 : line('.')
2853 let lnum = lnum1 + 1
2854 if a:0 > 1 && a:2 == 0
2855 let info = s:StageInfo(lnum - 1)
2856 if empty(info.paths) && len(info.section)
2857 while len(getline(lnum))
2866 while lnum > 0 && getline(lnum) =~# '^[ @\+-]'
2869 let info = s:StageInfo(lnum)
2870 if !has_key(b:fugitive_diff, info.section)
2873 if getline(lnum + 1) =~# '^[ @\+-]'
2874 let lnum2 = lnum + 1
2875 while getline(lnum2 + 1) =~# '^[ @\+-]'
2878 if a:mode !=# 'show'
2879 setlocal modifiable noreadonly
2880 exe 'silent keepjumps ' . (lnum + 1) . ',' . lnum2 . 'delete _'
2881 call remove(b:fugitive_expanded[info.section], info.filename)
2882 setlocal nomodifiable readonly nomodified
2886 if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADMRU]$' || a:mode ==# 'hide'
2893 for line in b:fugitive_diff[info.section]
2894 if mode ==# 'await' && line[0] ==# '@'
2895 let mode = 'capture'
2897 if mode !=# 'head' && line !~# '^[ @\+-]'
2903 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '--- ' . info.relative[-1]
2905 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '+++ ' . info.relative[0]
2907 elseif mode ==# 'capture'
2908 call add(diff, line)
2909 elseif line[0] ==# '@'
2915 setlocal modifiable noreadonly
2916 silent call append(lnum, diff)
2917 let b:fugitive_expanded[info.section][info.filename] = [start, len(diff)]
2918 setlocal nomodifiable readonly nomodified
2924 function! s:NextExpandedHunk(count) abort
2925 for i in range(a:count)
2926 call s:StageInline('show', line('.'), 1)
2927 call search(s:file_pattern . '\|^@','W')
2932 function! s:StageDiff(diff) abort
2933 let lnum = line('.')
2934 let info = s:StageInfo(lnum)
2935 let prefix = info.offset > 0 ? '+' . info.offset : ''
2936 if empty(info.paths) && info.section ==# 'Staged'
2937 return 'Git! diff --no-ext-diff --cached'
2938 elseif empty(info.paths)
2939 return 'Git! diff --no-ext-diff'
2940 elseif len(info.paths) > 1
2941 execute 'Gedit' . prefix s:fnameescape(':0:' . info.paths[0])
2942 return a:diff . '! HEAD:'.s:fnameescape(info.paths[1])
2943 elseif info.section ==# 'Staged' && info.sigil ==# '-'
2944 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2945 return a:diff . '! :0:%'
2946 elseif info.section ==# 'Staged'
2947 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2948 return a:diff . '! @:%'
2949 elseif info.sigil ==# '-'
2950 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2951 return a:diff . '! :(top)%'
2953 execute 'Gedit' prefix s:fnameescape(':(top)'.info.paths[0])
2958 function! s:StageDiffEdit() abort
2959 let info = s:StageInfo(line('.'))
2960 let arg = (empty(info.paths) ? s:Tree() : info.paths[0])
2961 if info.section ==# 'Staged'
2962 return 'Git! diff --no-ext-diff --cached '.s:fnameescape(arg)
2963 elseif info.status ==# '?'
2964 call s:TreeChomp('add', '--intent-to-add', '--', arg)
2965 return s:ReloadStatus()
2967 return 'Git! diff --no-ext-diff '.s:fnameescape(arg)
2971 function! s:StageApply(info, reverse, extra) abort
2972 if a:info.status ==# 'R'
2973 call s:throw('fugitive: patching renamed file not yet supported')
2975 let cmd = ['apply', '-p0', '--recount'] + a:extra
2977 let start = info.patch
2979 let lines = getline(start, end)
2980 if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
2983 while getline(end) =~# '^[-+ ]'
2985 if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2986 call add(lines, ' ' . getline(end)[1:-1])
2989 while start > 0 && getline(start) !~# '^@'
2991 if getline(start) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2992 call insert(lines, ' ' . getline(start)[1:-1])
2993 elseif getline(start) =~# '^@'
2994 call insert(lines, getline(start))
2998 throw 'fugitive: cold not find hunk'
2999 elseif getline(start) !~# '^@@ '
3000 throw 'fugitive: cannot apply conflict hunk'
3002 let i = b:fugitive_expanded[info.section][info.filename][0]
3004 while get(b:fugitive_diff[info.section], i, '@') !~# '^@'
3005 call add(head, b:fugitive_diff[info.section][i])
3008 call extend(lines, head, 'keep')
3009 let temp = tempname()
3010 call writefile(lines, temp)
3012 call add(cmd, '--reverse')
3014 call extend(cmd, ['--', temp])
3015 let [output, exec_error] = s:ChompError(cmd)
3019 call s:throw(output)
3022 function! s:StageDelete(lnum1, lnum2, count) abort
3026 for info in s:Selection(a:lnum1, a:lnum2)
3027 if empty(info.paths)
3030 if info.status ==# 'D'
3031 let undo = 'Gremove'
3032 elseif info.paths[0] =~# '/$'
3033 let err .= '|echoerr ' . string('fugitive: will not delete directory ' . string(info.relative[0]))
3036 let undo = 'Gread ' . s:TreeChomp('hash-object', '-w', '--', info.paths[0])[0:10]
3039 call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
3040 elseif info.status ==# '?'
3041 call s:TreeChomp('clean', '-f', '--', info.paths[0])
3043 call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
3045 call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
3046 elseif info.status =~# '[ADU]' &&
3047 \ get(b:fugitive_files[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, {'status': ''}).status =~# '[AU]'
3048 call s:TreeChomp('checkout', info.section ==# 'Staged' ? '--ours' : '--theirs', '--', info.paths[0])
3049 elseif info.status ==# 'U'
3050 call s:TreeChomp('rm', '--', info.paths[0])
3051 elseif info.status ==# 'A'
3052 call s:TreeChomp('rm', '-f', '--', info.paths[0])
3053 elseif info.section ==# 'Unstaged'
3054 call s:TreeChomp('checkout', '--', info.paths[0])
3056 call s:TreeChomp('checkout', 'HEAD^{}', '--', info.paths[0])
3058 call add(restore, ':Gsplit ' . s:fnameescape(info.relative[0]) . '|' . undo)
3061 let err .= '|echoerr ' . string(v:exception)
3066 exe s:ReloadStatus()
3067 call s:StageReveal()
3069 return 'checktime|redraw|echomsg ' . string('To restore, ' . join(restore, '|')) . err
3071 return 'checktime|redraw' . err
3075 function! s:StageIgnore(lnum1, lnum2, count) abort
3077 for info in s:Selection(a:lnum1, a:lnum2)
3078 call extend(paths, info.relative)
3080 call map(paths, '"/" . v:val')
3081 exe 'Gsplit' (a:count ? '.gitignore' : '.git/info/exclude')
3082 let last = line('$')
3083 if last == 1 && empty(getline(1))
3084 call setline(last, paths)
3086 call append(last, paths)
3092 function! s:DoToggleHeadHeader(value) abort
3093 exe 'edit' s:fnameescape(s:Dir())
3094 call search('\C^index$', 'wc')
3097 function! s:DoStageUnpushedHeading(heading) abort
3098 let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
3102 let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
3103 call feedkeys(':Gpush ' . remote . ' ' . 'HEAD:' . branch)
3106 function! s:DoToggleUnpushedHeading(heading) abort
3107 return s:DoStageUnpushedHeading(a:heading)
3110 function! s:DoStageUnpushed(record) abort
3111 let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
3115 let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
3116 call feedkeys(':Gpush ' . remote . ' ' . a:record.commit . ':' . branch)
3119 function! s:DoToggleUnpushed(record) abort
3120 return s:DoStageUnpushed(a:record)
3123 function! s:DoUnstageUnpulledHeading(heading) abort
3124 call feedkeys(':Grebase')
3127 function! s:DoToggleUnpulledHeading(heading) abort
3128 call s:DoUnstageUnpulledHeading(a:heading)
3131 function! s:DoUnstageUnpulled(record) abort
3132 call feedkeys(':Grebase ' . a:record.commit)
3135 function! s:DoToggleUnpulled(record) abort
3136 call s:DoUnstageUnpulled(a:record)
3139 function! s:DoUnstageUnpushed(record) abort
3140 call feedkeys(':Grebase --autosquash ' . a:record.commit . '^')
3143 function! s:DoToggleStagedHeading(...) abort
3144 call s:TreeChomp('reset', '-q')
3148 function! s:DoUnstageStagedHeading(heading) abort
3149 return s:DoToggleStagedHeading(a:heading)
3152 function! s:DoToggleUnstagedHeading(...) abort
3153 call s:TreeChomp('add', '-u')
3157 function! s:DoStageUnstagedHeading(heading) abort
3158 return s:DoToggleUnstagedHeading(a:heading)
3161 function! s:DoToggleUntrackedHeading(...) abort
3162 call s:TreeChomp('add', '.')
3166 function! s:DoStageUntrackedHeading(heading) abort
3167 return s:DoToggleUntrackedHeading(a:heading)
3170 function! s:DoToggleStaged(record) abort
3172 return s:StageApply(a:record, 1, ['--cached'])
3174 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3179 function! s:DoUnstageStaged(record) abort
3180 return s:DoToggleStaged(a:record)
3183 function! s:DoToggleUnstaged(record) abort
3184 if a:record.patch && a:record.status !=# 'A'
3185 return s:StageApply(a:record, 0, ['--cached'])
3187 call s:TreeChomp(['add', '-A', '--'] + a:record.paths)
3192 function! s:DoStageUnstaged(record) abort
3193 return s:DoToggleUnstaged(a:record)
3196 function! s:DoUnstageUnstaged(record) abort
3197 if a:record.status ==# 'A'
3198 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3205 function! s:DoToggleUntracked(record) abort
3206 call s:TreeChomp(['add', '--'] + a:record.paths)
3210 function! s:DoStageUntracked(record) abort
3211 return s:DoToggleUntracked(a:record)
3214 function! s:StagePatch(lnum1,lnum2) abort
3219 for lnum in range(a:lnum1,a:lnum2)
3220 let info = s:StageInfo(lnum)
3221 if empty(info.paths) && info.section ==# 'Staged'
3222 return 'Git reset --patch'
3223 elseif empty(info.paths) && info.section ==# 'Unstaged'
3224 return 'Git add --patch'
3225 elseif empty(info.paths) && info.section ==# 'Untracked'
3226 return 'Git add --interactive'
3227 elseif empty(info.paths)
3231 if info.section ==# 'Staged'
3232 let reset += info.relative
3233 elseif info.section ==# 'Untracked'
3234 let intend += info.paths
3235 elseif info.status !~# '^D'
3236 let add += info.relative
3241 call s:TreeChomp(['add', '--intent-to-add', '--'] + intend)
3244 execute "Git add --patch -- ".join(map(add,'s:fnameescape(v:val)'))
3247 execute "Git reset --patch -- ".join(map(reset,'s:fnameescape(v:val)'))
3250 return 'echoerr ' . string(v:exception)
3252 return s:ReloadStatus()
3255 " Section: :Gcommit, :Grevert
3257 function! s:CommitInteractive(line1, line2, range, bang, mods, args, patch) abort
3258 let status = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
3259 let status = len(status) ? status . '|' : ''
3261 return status . 'if search("^Unstaged")|exe "normal >"|exe "+"|endif'
3263 return status . 'if search("^Untracked\\|^Unstaged")|exe "+"|endif'
3267 function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
3268 let mods = substitute(s:Mods(a:mods), '\C\<tab\>', '-tab', 'g')
3269 let dir = a:0 ? a:1 : s:Dir()
3270 let tree = s:Tree(dir)
3271 let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
3272 let outfile = tempname()
3275 let command = 'set GIT_EDITOR=false& '
3277 let command = 'env GIT_EDITOR=false '
3281 while get(argv, i, '--') !=# '--'
3282 if argv[i] =~# '^-[apzsneiovq].'
3283 call insert(argv, argv[i][0:1])
3284 let argv[i+1] = '-' . argv[i+1][2:-1]
3289 let command .= s:UserCommand(dir, ['commit'] + argv)
3290 if (&autowrite || &autowriteall) && !a:0
3293 if s:HasOpt(argv, '-i', '--interactive')
3294 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 0)
3295 elseif s:HasOpt(argv, '-p', '--patch')
3296 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 1)
3298 let [error_string, exec_error] = s:TempCmd(outfile, command)
3299 let errors = split(error_string, "\n")
3301 if !has('gui_running')
3305 echo join(errors, "\n")
3306 if filereadable(outfile)
3307 echo join(readfile(outfile), "\n")
3309 call fugitive#ReloadStatus(dir, 1)
3312 let error = get(errors,-2,get(errors,-1,'!'))
3313 if error =~# 'false''\=\.$'
3315 while get(argv, i, '--') !=# '--'
3316 if argv[i] =~# '^\%(-[eips]\|-[CcFm].\+\|--edit\|--interactive\|--patch\|--signoff\|--reedit-message=.*\|--reuse-message=.*\|--file=.*\|--message=.*\)$'
3317 call remove(argv, i)
3318 elseif argv[i] =~# '^\%(-[CcFm]\|--reedit-message\|--reuse-message\|--file\|--message\)$'
3319 call remove(argv, i, i + 1)
3321 if argv[i] =~# '^--cleanup\>'
3327 call insert(argv, '--no-signoff', i)
3328 call insert(argv, '--no-interactive', i)
3329 call insert(argv, '--no-edit', i)
3330 if !exists('cleanup')
3331 call insert(argv, '--cleanup=strip')
3333 call extend(argv, ['-F', msgfile], 'keep')
3334 if (bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&modified) || a:line2 == 0
3335 execute mods . 'keepalt edit' s:fnameescape(msgfile)
3336 elseif s:HasOpt(argv, '-v') || mods =~# '\<tab\>'
3337 execute mods . 'keepalt -tabedit' s:fnameescape(msgfile)
3339 execute mods . 'keepalt split' s:fnameescape(msgfile)
3341 let b:fugitive_commit_arguments = argv
3342 setlocal bufhidden=wipe filetype=gitcommit
3344 elseif empty(errors)
3345 let out = readfile(outfile)
3346 echo get(out, -1, '') =~# 'stash\|\d' ? get(out, -2, '') : get(out, -1, '')
3349 echo join(errors, "\n")
3354 return 'echoerr ' . string(v:exception)
3356 call delete(outfile)
3360 function! s:RevertSubcommand(line1, line2, range, bang, mods, args) abort
3362 let no_commit = s:HasOpt(a:args, '-n', '--no-commit', '--no-edit', '--abort', '--continue', '--quit')
3363 let cmd = s:UserCommand(dir, ['revert'] + (no_commit ? [] : ['-n']) + a:args)
3364 let [out, exec_error] = s:SystemError(cmd)
3365 call fugitive#ReloadStatus(dir, 1)
3366 if no_commit || exec_error
3367 return 'echo ' . string(substitute(out, "\n$", '', ''))
3369 return s:CommitSubcommand(a:line1, a:line2, a:range, a:bang, a:mods, [], dir)
3372 function! fugitive#CommitComplete(A, L, P) abort
3373 if a:A =~# '^--fixup=\|^--squash='
3374 let commits = s:LinesError(['log', '--pretty=format:%s', '@{upstream}..'])[0]
3375 let pre = matchstr(a:A, '^--\w*=''\=') . ':/^'
3377 call map(commits, 'pre . string(tr(v:val, "|\"^$*[]", "......."))[1:-1]')
3378 call filter(commits, 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
3381 return s:FilterEscape(map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")'), a:A)
3384 return s:CompleteSub('commit', a:A, a:L, a:P, function('fugitive#CompletePath'))
3389 function! fugitive#RevertComplete(A, L, P) abort
3390 return s:CompleteSub('revert', a:A, a:L, a:P, function('s:CompleteRevision'))
3393 function! s:FinishCommit() abort
3394 let buf = +expand('<abuf>')
3395 let args = getbufvar(buf, 'fugitive_commit_arguments')
3397 call setbufvar(buf, 'fugitive_commit_arguments', [])
3398 if getbufvar(buf, 'fugitive_commit_rebase')
3399 call setbufvar(buf, 'fugitive_commit_rebase', 0)
3400 let s:rebase_continue = s:Dir(buf)
3402 return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf))
3407 " Section: :Gmerge, :Grebase, :Gpull
3409 function! fugitive#MergeComplete(A, L, P) abort
3410 return s:CompleteSub('merge', a:A, a:L, a:P, function('s:CompleteRevision'))
3413 function! fugitive#RebaseComplete(A, L, P) abort
3414 return s:CompleteSub('rebase', a:A, a:L, a:P, function('s:CompleteRevision'))
3417 function! fugitive#PullComplete(A, L, P) abort
3418 return s:CompleteSub('pull', a:A, a:L, a:P, function('s:CompleteRemote'))
3421 function! s:RebaseSequenceAborter() abort
3422 if !exists('s:rebase_sequence_aborter')
3423 let temp = tempname() . '.sh'
3426 \ 'echo exec false | cat - "$1" > "$1.fugitive"',
3427 \ 'mv "$1.fugitive" "$1"'],
3429 let s:rebase_sequence_aborter = temp
3431 return s:rebase_sequence_aborter
3434 function! fugitive#Cwindow() abort
3435 if &buftype == 'quickfix'
3439 if &buftype == 'quickfix'
3445 let s:common_efm = ''
3447 \ . '%+Eusage:%.%#,'
3448 \ . '%+Eerror:%.%#,'
3449 \ . '%+Efatal:%.%#,'
3450 \ . '%-G%.%#%\e[K%.%#,'
3451 \ . '%-G%.%#%\r%.%\+'
3453 let s:rebase_abbrevs = {
3467 function! s:RebaseEdit(cmd, dir) abort
3468 let rebase_todo = s:fnameescape(fugitive#Find('.git/rebase-merge/git-rebase-todo', a:dir))
3470 if filereadable(rebase_todo)
3471 let new = readfile(rebase_todo)
3475 for i in range(len(new))
3476 if new[i] =~# '^\l\+\s\+[0-9a-f]\{5,\}\>'
3477 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3479 let sha_length = len(s:TreeChomp(a:dir, 'rev-parse', '--short', sha))
3481 let shortened_sha = strpart(sha, 0, sha_length)
3482 let shas[shortened_sha] = sha
3483 let new[i] = substitute(new[i], sha, shortened_sha, '')
3486 call writefile(new, rebase_todo)
3488 return a:cmd . ' +setlocal\ bufhidden=wipe\|' . escape('let b:fugitive_rebase_shas = ' . string(shas), ' ') . ' ' . rebase_todo
3491 function! s:MergeRebase(cmd, bang, mods, args, ...) abort
3492 let dir = a:0 ? a:1 : s:Dir()
3494 let mods = s:Mods(a:mods)
3495 if a:cmd =~# '^rebase' && s:HasOpt(args, '-i', '--interactive')
3496 let cmd = fugitive#Prepare(dir, '-c', 'sequence.editor=sh ' . s:RebaseSequenceAborter(), 'rebase') . ' ' . s:shellesc(args)
3497 let out = system(cmd)[0:-2]
3498 for file in ['end', 'msgnum']
3499 let file = fugitive#Find('.git/rebase-merge/' . file, dir)
3500 if !filereadable(file)
3501 return 'echoerr ' . string("fugitive: " . out)
3503 call writefile([readfile(file)[0] - 1], file)
3505 call writefile([], fugitive#Find('.git/rebase-merge/done', dir))
3509 return s:RebaseEdit(mods . 'split', dir)
3510 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--edit-todo') && filereadable(fugitive#Find('.git/rebase-merge/git-rebase-todo', dir))
3511 return s:RebaseEdit(mods . 'split', dir)
3512 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--continue') && !a:0
3513 let rdir = fugitive#Find('.git/rebase-merge', dir)
3514 let exec_error = s:ChompError([dir, 'diff-index', '--cached', '--quiet', 'HEAD', '--'])[1]
3515 if exec_error && isdirectory(rdir)
3516 if getfsize(rdir . '/amend') <= 0
3517 return 'exe ' . string(mods . 'Gcommit -n -F ' . s:fnameescape(rdir .'/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3518 elseif readfile(rdir . '/amend')[0] ==# fugitive#Head(-1, dir)
3519 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(rdir . '/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3523 let had_merge_msg = filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3526 let argv += s:AskPassArgs(dir) + ['pull', '--progress']
3528 call add(argv, a:cmd)
3530 if !s:HasOpt(args, '--no-edit', '--abort', '-m') && a:cmd !=# 'rebase'
3531 call add(argv, '--edit')
3533 if a:cmd ==# 'rebase' && s:HasOpt(args, '--autosquash') && !s:HasOpt(args, '--interactive', '-i')
3534 call add(argv, '--interactive')
3536 call extend(argv, args)
3538 let [mp, efm] = [&l:mp, &l:efm]
3540 let cdback = s:Cd(s:Tree(dir))
3541 let &l:errorformat = ''
3542 \ . '%-Gerror:%.%#false''.,'
3543 \ . '%-G%.%# ''git commit'' %.%#,'
3544 \ . '%+Emerge:%.%#,'
3545 \ . s:common_efm . ','
3546 \ . '%+ECannot %.%#: You have unstaged changes.,'
3547 \ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
3548 \ . '%+EThere is no tracking information for the current branch.,'
3549 \ . '%+EYou are not currently on a branch. Please specify which,'
3550 \ . '%+I %#git rebase --continue,'
3551 \ . 'CONFLICT (%m): %f deleted in %.%#,'
3552 \ . 'CONFLICT (%m): Merge conflict in %f,'
3553 \ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
3554 \ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
3555 \ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
3556 \ . '%+ECONFLICT %.%#,'
3557 \ . '%+EKONFLIKT %.%#,'
3558 \ . '%+ECONFLIT %.%#,'
3559 \ . "%+EXUNG \u0110\u1ed8T %.%#,"
3560 \ . "%+E\u51b2\u7a81 %.%#,"
3562 if a:cmd =~# '^merge' && empty(args) &&
3563 \ (had_merge_msg || isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
3564 \ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
3565 let cmd = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
3567 let cmd = s:UserCommand(dir, argv)
3569 if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
3570 let old_sequence_editor = $GIT_SEQUENCE_EDITOR
3571 let $GIT_SEQUENCE_EDITOR = 'true'
3573 let cmd = 'env GIT_SEQUENCE_EDITOR=true ' . cmd
3575 if !empty($GIT_EDITOR) || has('win32')
3576 let old_editor = $GIT_EDITOR
3577 let $GIT_EDITOR = 'false'
3579 let cmd = 'env GIT_EDITOR=false ' . substitute(cmd, '^env ', '', '')
3581 if !has('patch-8.1.0334') && has('terminal') && &autowrite
3582 let autowrite_was_set = 1
3586 let &l:makeprg = cmd
3587 silent noautocmd make!
3588 catch /^Vim\%((\a\+)\)\=:E211/
3589 let err = v:exception
3591 if exists('autowrite_was_set')
3595 let [&l:mp, &l:efm] = [mp, efm]
3596 if exists('old_editor')
3597 let $GIT_EDITOR = old_editor
3599 if exists('old_sequence_editor')
3600 let $GIT_SEQUENCE_EDITOR = old_sequence_editor
3604 call fugitive#ReloadStatus(dir, 1)
3605 if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
3606 if a:cmd =~# '^rebase' &&
3607 \ filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) &&
3608 \ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) &&
3609 \ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^e]'
3611 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1'
3612 elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3614 return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir))
3617 let qflist = getqflist()
3622 let e.pattern = '^<<<<<<<'
3625 call fugitive#Cwindow()
3627 call setqflist(qflist, 'r')
3633 return exists('err') ? 'echoerr '.string(err) : 'exe'
3636 function! s:RebaseClean(file) abort
3637 if !filereadable(a:file)
3640 let old = readfile(a:file)
3642 for i in range(len(new))
3643 let new[i] = substitute(new[i], '^\l\>', '\=get(s:rebase_abbrevs,submatch(0),submatch(0))', '')
3645 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3646 let rebase_shas = getbufvar(a:file, 'fugitive_rebase_shas')
3647 if len(sha) && type(rebase_shas) == type({}) && has_key(rebase_shas, sha)
3648 let new[i] = substitute(new[i], '\C\<' . sha . '\>', rebase_shas[sha], '')
3652 call writefile(new, a:file)
3657 function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort
3658 return s:MergeRebase('merge', a:bang, a:mods, a:args)
3661 function! s:RebaseSubcommand(line1, line2, range, bang, mods, args) abort
3662 return s:MergeRebase('rebase', a:bang, a:mods, a:args)
3665 function! s:PullSubcommand(line1, line2, range, bang, mods, args) abort
3666 return s:MergeRebase('pull', a:bang, a:mods, a:args)
3669 augroup fugitive_merge
3671 autocmd VimLeavePre,BufDelete git-rebase-todo
3672 \ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
3673 \ call s:RebaseClean(expand('<afile>')) |
3674 \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
3675 \ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) |
3678 autocmd BufEnter * nested
3679 \ if exists('s:rebase_continue') |
3680 \ exe s:MergeRebase('rebase', 0, '', [getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort'], remove(s:, 'rebase_continue')) |
3684 " Section: :Ggrep, :Glog
3686 if !exists('g:fugitive_summary_format')
3687 let g:fugitive_summary_format = '%s'
3690 function! fugitive#GrepComplete(A, L, P) abort
3691 return s:CompleteSub('grep', a:A, a:L, a:P)
3694 function! fugitive#LogComplete(A, L, P) abort
3695 return s:CompleteSub('log', a:A, a:L, a:P)
3698 function! s:GrepParseLine(prefix, name_only, dir, line) abort
3699 let entry = {'valid': 1}
3700 let match = matchlist(a:line, '^\(.\{-\}\):\(\d\+\):\(\d\+:\)\=\(.*\)$')
3702 let entry.module = match[1]
3703 let entry.lnum = +match[2]
3704 let entry.col = +match[3]
3705 let entry.text = match[4]
3706 elseif a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
3707 return {'text': a:line}
3709 let entry.module = matchstr(a:line, '\CBinary file \zs.*\ze matches$')
3710 if len(entry.module)
3711 let entry.text = 'Binary file'
3715 if empty(entry.module) && a:name_only
3716 let entry.module = a:line
3718 if empty(entry.module)
3719 return {'text': a:line}
3721 if entry.module !~# ':'
3722 let entry.filename = a:prefix . entry.module
3724 let entry.filename = fugitive#Find(entry.module, a:dir)
3729 function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
3732 let listnr = a:line1 == 0 ? a:line1 : a:line2
3733 let cmd = ['--no-pager', 'grep', '-n', '--no-color', '--full-name']
3734 if fugitive#GitVersion(2, 19)
3735 call add(cmd, '--column')
3737 let tree = s:Tree(dir)
3738 if type(a:args) == type([])
3739 let [args, after] = [a:args, '']
3741 let [args, after] = s:SplitExpandChain(a:args, tree)
3743 let prefix = FugitiveVimPath(s:HasOpt(args, '--cached') || empty(tree) ? 'fugitive://' . dir . '//0/' : tree . '/')
3744 let name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
3745 let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
3747 exe listnr 'wincmd w'
3752 call s:QuickfixCreate(listnr, {'title': (listnr < 0 ? ':Ggrep ' : ':Glgrep ') . s:fnameescape(args)})
3753 let tempfile = tempname()
3754 let event = listnr < 0 ? 'grep-fugitive' : 'lgrep-fugitive'
3755 silent exe s:DoAutocmd('QuickFixCmdPre ' . event)
3756 exe '!' . escape(s:UserCommand(dir, cmd + args), '%#!')
3757 \ printf(&shellpipe . (&shellpipe =~# '%s' ? '' : ' %s'), s:shellesc(tempfile))
3758 let list = map(readfile(tempfile), 's:GrepParseLine(prefix, name_only, dir, v:val)')
3759 call s:QuickfixSet(listnr, list, 'a')
3760 silent exe s:DoAutocmd('QuickFixCmdPost ' . event)
3761 if !has('gui_running')
3764 if !a:bang && !empty(list)
3765 return (listnr < 0 ? 'c' : 'l').'first' . after
3771 function! s:LogFlushQueue(state) abort
3772 let queue = remove(a:state, 'queue')
3773 if a:state.child_found
3774 call remove(queue, 0)
3776 if len(queue) && queue[-1] ==# {'text': ''}
3777 call remove(queue, -1)
3782 function! s:LogParse(state, dir, line) abort
3783 if a:state.context ==# 'hunk' && a:line =~# '^[-+ ]'
3786 let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
3788 let a:state.context = 'commit'
3789 let a:state.base = 'fugitive://' . a:dir . '//' . list[2]
3790 let a:state.base_module = len(list[1]) ? list[1] : list[2]
3791 let a:state.message = list[3]
3792 if has_key(a:state, 'diffing')
3793 call remove(a:state, 'diffing')
3795 let queue = s:LogFlushQueue(a:state)
3796 let a:state.queue = [{
3798 \ 'filename': a:state.base . a:state.target,
3799 \ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
3800 \ 'text': a:state.message}]
3801 let a:state.child_found = 0
3803 elseif type(a:line) == type(0)
3804 return s:LogFlushQueue(a:state)
3805 elseif a:line =~# '^diff'
3806 let a:state.context = 'diffhead'
3807 elseif a:line =~# '^[+-]\{3\} \w/' && a:state.context ==# 'diffhead'
3808 let a:state.diffing = a:line[5:-1]
3809 elseif a:line =~# '^@@[^@]*+\d' && has_key(a:state, 'diffing') && has_key(a:state, 'base')
3810 let a:state.context = 'hunk'
3811 if empty(a:state.target) || a:state.target ==# a:state.diffing
3812 let a:state.child_found = 1
3813 call add(a:state.queue, {
3815 \ 'filename': a:state.base . a:state.diffing,
3816 \ 'module': a:state.base_module . substitute(a:state.diffing, '^/', ':', ''),
3817 \ 'lnum': +matchstr(a:line, '+\zs\d\+'),
3818 \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
3820 elseif a:state.follow &&
3821 \ a:line =~# '^ \%(mode change \d\|\%(create\|delete\) mode \d\|\%(rename\|copy\|rewrite\) .* (\d\+%)$\)'
3822 let rename = matchstr(a:line, '^ rename \zs.* => .*\ze (\d\+%)$')
3824 let rename = rename =~# '{.* => .*}' ? rename : '{' . rename . '}'
3825 if a:state.target ==# simplify('/' . substitute(rename, '{.* => \(.*\)}', '\1', ''))
3826 let a:state.target = simplify('/' . substitute(rename, '{\(.*\) => .*}', '\1', ''))
3829 if !get(a:state, 'ignore_summary')
3830 call add(a:state.queue, {'text': a:line})
3832 elseif a:state.context ==# 'commit' || a:state.context ==# 'init'
3833 call add(a:state.queue, {'text': a:line})
3838 function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
3841 let listnr = a:type =~# '^l' ? 0 : -1
3842 let [args, after] = s:SplitExpandChain(a:args, s:Tree(dir))
3843 let split = index(args, '--')
3845 let paths = args[split : -1]
3846 let args = args[0 : split - 1]
3853 if a:line1 == 0 && a:count
3854 let path = fugitive#Path(bufname(a:count), '/', dir)
3856 let path = fugitive#Path(@%, '/', dir)
3862 let state = {'context': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
3863 if path =~# '^/\.git\%(/\|$\)\|^$'
3866 let range = "0," . (a:count ? a:count : bufnr(''))
3867 let extra = ['.' . path]
3868 if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
3869 let state.follow = 1
3870 if !s:HasOpt(args, '--follow')
3871 call insert(args, '--follow')
3873 if !s:HasOpt(args, '--summary')
3874 call insert(args, '--summary')
3875 let state.ignore_summary = 1
3879 if !s:HasOpt(args, '--merges', '--no-merges')
3880 call insert(args, '--no-merges')
3882 call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
3884 if len(path) && empty(filter(copy(args), 'v:val =~# "^[^-]"'))
3885 let owner = s:Owner(@%, dir)
3887 call add(args, owner)
3893 if s:HasOpt(args, '-g', '--walk-reflogs')
3894 let format = "%gd\t%H %gs"
3896 let format = "%h\t%H " . g:fugitive_summary_format
3898 let cmd = ['--no-pager']
3899 if fugitive#GitVersion(1, 9)
3900 call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'])
3902 call extend(cmd, ['log', '-U0', '--no-patch'])
3905 \ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
3906 \ args + paths + extra)
3907 let state.target = path
3908 let title = (listnr < 0 ? ':Gclog ' : ':Gllog ') . s:fnameescape(args + paths)
3909 if empty(paths + extra) && empty(a:type) && len(s:Relative('/'))
3910 let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after
3912 return s:QuickfixStream(listnr, title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after
3915 " Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
3917 function! s:UsableWin(nr) abort
3918 return a:nr && !getwinvar(a:nr, '&previewwindow') && !getwinvar(a:nr, '&winfixwidth') &&
3919 \ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
3920 \ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
3921 \ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
3924 function! s:OpenParse(args, wants_cmd) abort
3927 let args = copy(a:args)
3929 if args[0] =~# '^++'
3930 call add(opts, ' ' . escape(remove(args, 0), ' |"'))
3931 elseif a:wants_cmd && args[0] =~# '^+'
3932 call add(cmds, remove(args, 0)[1:-1])
3938 let file = join(args)
3939 elseif empty(expand('%'))
3941 elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>'
3947 let efile = s:Expand(file)
3948 let url = fugitive#Find(efile, dir)
3950 if a:wants_cmd && file[0] ==# '>' && efile[0] !=# '>' && get(b:, 'fugitive_type', '') isnot# 'tree' && &filetype !=# 'netrw'
3951 let line = line('.')
3952 if expand('%:p') !=# url
3953 let diffcmd = 'diff'
3954 let from = s:DirRev(@%)[1]
3955 let to = s:DirRev(url)[1]
3956 if empty(from) && empty(to)
3957 let diffcmd = 'diff-files'
3958 let args = ['--', expand('%:p'), url]
3960 let args = [from, '--', url]
3962 let args = [to, '--', expand('%:p')]
3965 let args = [from, to]
3967 let [res, exec_error] = s:LinesError([dir, diffcmd, '-U0'] + args)
3969 call filter(res, 'v:val =~# "^@@ "')
3970 call map(res, 'substitute(v:val, ''[-+]\d\+\zs '', ",1 ", "g")')
3971 call map(res, 'matchlist(v:val, ''^@@ -\(\d\+\),\(\d\+\) +\(\d\+\),\(\d\+\) @@'')[1:4]')
3972 if exists('reverse')
3973 call map(res, 'v:val[2:3] + v:val[0:1]')
3975 call filter(res, 'v:val[0] < '.line('.'))
3976 let hunk = get(res, -1, [0,0,0,0])
3977 if hunk[0] + hunk[1] > line('.')
3978 let line = hunk[2] + max([1 - hunk[3], 0])
3980 let line = hunk[2] + max([hunk[3], 1]) + line('.') - hunk[0] - max([hunk[1], 1])
3984 call insert(cmds, line)
3987 let pre = join(opts, '')
3989 let pre .= ' +' . escape(join(map(cmds, '"exe ".string(v:val)'), '|'), ' |"')
3991 let pre .= ' +' . escape(cmds[0], ' |"')
3996 function! s:DiffClose() abort
3997 let mywinnr = winnr()
3998 for winnr in [winnr('#')] + range(winnr('$'),1,-1)
3999 if winnr != mywinnr && getwinvar(winnr,'&diff')
4000 execute winnr.'wincmd w'
4010 function! s:BlurStatus() abort
4011 if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
4012 let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
4014 exe winnrs[0].'wincmd w'
4024 function! s:OpenExec(cmd, mods, args, ...) abort
4025 let dir = a:0 ? s:Dir(a:1) : s:Dir()
4026 let temp = tempname()
4027 let columns = get(g:, 'fugitive_columns', 80)
4031 let env = 'set COLUMNS=' . columns . '& '
4033 let env = 'env COLUMNS=' . columns . ' '
4035 silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
4036 \ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
4038 let temp = s:Resolve(temp)
4039 let first = join(readfile(temp, '', 2), "\n")
4040 if first =~# '\<\([[:upper:][:digit:]_-]\+(\d\+)\).*\1'
4041 let filetype = 'man'
4043 let filetype = 'git'
4045 let s:temp_files[s:cpath(temp)] = { 'dir': dir, 'filetype': filetype, 'modifiable': first =~# '^diff ' }
4049 silent execute s:Mods(a:mods) . a:cmd temp
4050 call fugitive#ReloadStatus(dir, 1)
4051 return 'echo ' . string(':!' . s:UserCommand(dir, a:args))
4054 function! fugitive#Open(cmd, bang, mods, arg, args) abort
4056 return s:OpenExec(a:cmd, a:mods, s:SplitExpand(a:arg, s:Tree()))
4059 let mods = s:Mods(a:mods)
4061 let [file, pre] = s:OpenParse(a:args, 1)
4063 return 'echoerr ' . string(v:exception)
4065 if file !~# '^\a\a\+:'
4066 let file = s:sub(file, '/$', '')
4071 return mods . a:cmd . pre . ' ' . s:fnameescape(file)
4074 function! fugitive#ReadCommand(line1, count, range, bang, mods, arg, args) abort
4075 let mods = s:Mods(a:mods)
4078 let delete = 'silent 1,' . line('$') . 'delete_|'
4079 let after = line('$')
4081 let delete = 'silent ' . a:line1 . ',' . a:count . 'delete_|'
4087 let args = s:SplitExpand(a:arg, s:Tree(dir))
4088 silent execute mods . after . 'read!' escape(s:UserCommand(dir, ['--no-pager'] + args), '!#%')
4089 execute delete . 'diffupdate'
4090 call fugitive#ReloadStatus(dir, 1)
4091 return 'redraw|echo '.string(':!'.s:UserCommand(dir, args))
4094 let [file, pre] = s:OpenParse(a:args, 0)
4096 return 'echoerr ' . string(v:exception)
4098 if file =~# '^fugitive:' && after is# 0
4099 return 'exe ' .string(mods . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
4102 exe after . 'foldopen!'
4104 return mods . after . 'read' . pre . ' ' . s:fnameescape(file) . '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')
4107 function! fugitive#ReadComplete(A, L, P) abort
4109 return fugitive#Complete(a:A, a:L, a:P)
4111 return fugitive#CompleteObject(a:A, a:L, a:P)
4115 " Section: :Gwrite, :Gwq
4117 function! fugitive#WriteCommand(line1, line2, range, bang, mods, arg, args) abort
4118 if exists('b:fugitive_commit_arguments')
4119 return 'write|bdelete'
4120 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
4122 elseif get(b:, 'fugitive_type', '') ==# 'index'
4124 elseif &buftype ==# 'nowrite' && getline(4) =~# '^+++ '
4125 let filename = getline(4)[6:-1]
4128 setlocal buftype=nowrite
4129 if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6]
4130 let [message, exec_error] = s:ChompError(['apply', '--cached', '--reverse', '--', expand('%:p')])
4132 let [message, exec_error] = s:ChompError(['apply', '--cached', '--', expand('%:p')])
4142 return 'Gedit '.fnameescape(filename)
4145 let mytab = tabpagenr()
4146 let mybufnr = bufnr('')
4148 let file = len(a:args) ? s:Generate(s:Expand(join(a:args, ' '))) : fugitive#Real(@%)
4150 return 'echoerr ' . string(v:exception)
4153 return 'echoerr '.string('fugitive: cannot determine file path')
4155 if file =~# '^fugitive:'
4156 return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
4159 let always_permitted = s:cpath(fugitive#Real(@%), file) && empty(s:DirCommitFile(@%)[1])
4160 if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
4161 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
4162 return 'echoerr v:errmsg'
4165 for nr in range(1,bufnr('$'))
4166 if fnamemodify(bufname(nr),':p') ==# file
4171 if treebufnr > 0 && treebufnr != bufnr('')
4172 let temp = tempname()
4173 silent execute 'keepalt %write '.temp
4174 for tab in [mytab] + range(1,tabpagenr('$'))
4175 for winnr in range(1,tabpagewinnr(tab,'$'))
4176 if tabpagebuflist(tab)[winnr-1] == treebufnr
4177 execute 'tabnext '.tab
4179 execute winnr.'wincmd w'
4180 let restorewinnr = 1
4183 let lnum = line('.')
4184 let last = line('$')
4185 silent execute '$read '.temp
4186 silent execute '1,'.last.'delete_'
4192 if exists('restorewinnr')
4195 execute 'tabnext '.mytab
4202 call writefile(readfile(temp,'b'),file,'b')
4205 execute 'write! '.s:fnameescape(file)
4209 let [error, exec_error] = s:ChompError(['add', '--force', '--', file])
4211 let [error, exec_error] = s:ChompError(['add', '--', file])
4214 let v:errmsg = 'fugitive: '.error
4215 return 'echoerr v:errmsg'
4217 if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
4221 let one = s:Generate(':1:'.file)
4222 let two = s:Generate(':2:'.file)
4223 let three = s:Generate(':3:'.file)
4224 for nr in range(1,bufnr('$'))
4225 let name = fnamemodify(bufname(nr), ':p')
4226 if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three)
4227 execute nr.'bdelete'
4232 let zero = s:Generate(':0:'.file)
4233 silent exe s:DoAutocmd('BufWritePost ' . s:fnameescape(zero))
4234 for tab in range(1,tabpagenr('$'))
4235 for winnr in range(1,tabpagewinnr(tab,'$'))
4236 let bufnr = tabpagebuflist(tab)[winnr-1]
4237 let bufname = fnamemodify(bufname(bufnr), ':p')
4238 if bufname ==# zero && bufnr != mybufnr
4239 execute 'tabnext '.tab
4241 execute winnr.'wincmd w'
4242 let restorewinnr = 1
4245 let lnum = line('.')
4246 let last = line('$')
4247 silent execute '$read '.s:fnameescape(file)
4248 silent execute '1,'.last.'delete_'
4253 if exists('restorewinnr')
4256 execute 'tabnext '.mytab
4262 call fugitive#ReloadStatus(-1, 1)
4266 function! fugitive#WqCommand(...) abort
4267 let bang = a:4 ? '!' : ''
4268 if exists('b:fugitive_commit_arguments')
4271 let result = call('fugitive#WriteCommand', a:000)
4272 if result =~# '^\%(write\|wq\|echoerr\)'
4273 return s:sub(result,'^write','wq')
4275 return result.'|quit'.bang
4279 augroup fugitive_commit
4281 autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute substitute(s:FinishCommit(), '\C^echoerr \(''[^'']*''\)*', 'redraw|echohl ErrorMsg|echo \1|echohl NONE', '')
4284 " Section: :Gpush, :Gfetch
4286 function! fugitive#PushComplete(A, L, P) abort
4287 return s:CompleteSub('push', a:A, a:L, a:P, function('s:CompleteRemote'))
4290 function! fugitive#FetchComplete(A, L, P) abort
4291 return s:CompleteSub('fetch', a:A, a:L, a:P, function('s:CompleteRemote'))
4294 function! s:AskPassArgs(dir) abort
4295 if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) &&
4296 \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#Config('core.askPass', a:dir))
4297 if s:executable(s:ExecPath() . '/git-gui--askpass')
4298 return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass']
4299 elseif s:executable('ssh-askpass')
4300 return ['-c', 'core.askPass=ssh-askpass']
4306 function! s:Dispatch(bang, cmd, args) abort
4308 let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
4310 let b:current_compiler = 'git'
4311 let &l:errorformat = s:common_efm .
4312 \ ',%\&git_dir=' . escape(substitute(dir, '%', '%%', 'g'), '\,')
4313 let &l:makeprg = s:UserCommand(dir, s:AskPassArgs(dir) + [a:cmd] + a:args)
4314 if exists(':Make') == 2
4318 if !has('patch-8.1.0334') && has('terminal') && &autowrite
4319 let autowrite_was_set = 1
4323 silent noautocmd make!
4325 return 'call fugitive#Cwindow()|silent ' . s:DoAutocmd('ShellCmdPost')
4328 let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
4329 if empty(cc) | unlet! b:current_compiler | endif
4330 if exists('autowrite_was_set')
4336 function! s:PushSubcommand(line1, line2, range, bang, mods, args) abort
4337 return s:Dispatch(a:bang ? '!' : '', 'push', a:args)
4340 function! s:FetchSubcommand(line1, line2, range, bang, mods, args) abort
4341 return s:Dispatch(a:bang ? '!' : '', 'fetch', a:args)
4346 augroup fugitive_diff
4348 autocmd BufWinLeave *
4349 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
4350 \ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
4352 autocmd BufWinEnter *
4353 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 1 |
4354 \ call s:diffoff() |
4358 function! s:can_diffoff(buf) abort
4359 return getwinvar(bufwinnr(a:buf), '&diff') &&
4360 \ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
4363 function! fugitive#CanDiffoff(buf) abort
4364 return s:can_diffoff(bufnr(a:buf))
4367 function! s:diff_modifier(count) abort
4368 let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
4369 if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
4371 elseif &diffopt =~# 'vertical'
4373 elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
4380 function! s:diff_window_count() abort
4382 for nr in range(1,winnr('$'))
4383 let c += getwinvar(nr,'&diff')
4388 function! s:diff_restore() abort
4389 let restore = 'setlocal nodiff noscrollbind'
4390 \ . ' scrollopt=' . &l:scrollopt
4391 \ . (&l:wrap ? ' wrap' : ' nowrap')
4392 \ . ' foldlevel=999'
4393 \ . ' foldmethod=' . &l:foldmethod
4394 \ . ' foldcolumn=' . &l:foldcolumn
4395 \ . ' foldlevel=' . &l:foldlevel
4396 \ . (&l:foldenable ? ' foldenable' : ' nofoldenable')
4397 if has('cursorbind')
4398 let restore .= (&l:cursorbind ? ' ' : ' no') . 'cursorbind'
4403 function! s:diffthis() abort
4405 let w:fugitive_diff_restore = s:diff_restore()
4410 function! s:diffoff() abort
4411 if exists('w:fugitive_diff_restore')
4412 execute w:fugitive_diff_restore
4413 unlet w:fugitive_diff_restore
4419 function! s:diffoff_all(dir) abort
4420 let curwin = winnr()
4421 for nr in range(1,winnr('$'))
4422 if getwinvar(nr,'&diff')
4424 execute nr.'wincmd w'
4425 let restorewinnr = 1
4427 if s:Dir() ==# a:dir
4432 execute curwin.'wincmd w'
4435 function! s:CompareAge(mine, theirs) abort
4436 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
4437 let mine = substitute(a:mine, '^:', '', '')
4438 let theirs = substitute(a:theirs, '^:', '', '')
4439 let my_score = get(scores, ':'.mine, 0)
4440 let their_score = get(scores, ':'.theirs, 0)
4441 if my_score || their_score
4442 return my_score < their_score ? -1 : my_score != their_score
4443 elseif mine ==# theirs
4446 let base = s:TreeChomp('merge-base', mine, theirs)
4449 elseif base ==# theirs
4452 let my_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:mine, '--')
4453 let their_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:theirs, '--')
4454 return my_time < their_time ? -1 : my_time != their_time
4457 function! s:IsConflicted() abort
4458 return len(@%) && !empty(s:ChompDefault('', 'ls-files', '--unmerged', '--', expand('%:p')))
4461 function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, args) abort
4462 let args = copy(a:args)
4464 if get(args, 0) =~# '^+'
4465 let post = remove(args, 0)[1:-1]
4467 if exists(':DiffGitCached') && empty(args)
4468 return s:Mods(a:mods) . 'DiffGitCached' . (len(post) ? '|' . post : '')
4470 let commit = s:DirCommitFile(@%)[1]
4471 if a:mods =~# '\<tab\>'
4472 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4473 let pre = 'tab split'
4475 let mods = 'keepalt ' . a:mods
4478 let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
4479 if (empty(args) || args[0] ==# ':') && a:keepfocus
4481 if empty(commit) && s:IsConflicted()
4482 let parents = [s:Relative(':2:'), s:Relative(':3:')]
4483 elseif empty(commit)
4484 let parents = [s:Relative(':0:')]
4485 elseif commit =~# '^\d\=$'
4486 let parents = [s:Relative('HEAD:')]
4487 elseif commit =~# '^\x\x\+$'
4488 let parents = s:LinesError(['rev-parse', commit . '^@'])[0]
4489 call map(parents, 's:Relative(v:val . ":")')
4493 if exists('parents') && len(parents) > 1
4495 let mods = (a:autodir ? s:diff_modifier(len(parents) + 1) : '') . s:Mods(mods, 'leftabove')
4497 execute mods 'split' s:fnameescape(s:Generate(parents[0]))
4498 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4502 call s:Map('n', 'd2o', ':diffget '.nr2.'<Bar>diffupdate<CR>', '<silent>')
4503 let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
4504 for i in range(len(parents)-1, 1, -1)
4505 execute mods 'split' s:fnameescape(s:Generate(parents[i]))
4506 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4510 call s:Map('n', 'd' . (i + 2) . 'o', ':diffget '.nrx.'<Bar>diffupdate<CR>', '<silent>')
4518 let arg = join(args, ' ')
4523 let file = s:Relative()
4526 let file = s:Relative(':0:')
4527 elseif arg =~# '^:\d$'
4529 let file = s:Relative(arg . ':')
4532 let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
4534 return 'echoerr ' . string(v:exception)
4537 elseif exists('parents') && len(parents)
4538 let file = parents[-1]
4540 let file = s:Relative()
4541 elseif s:IsConflicted()
4542 let file = s:Relative(':1:')
4543 let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
4546 let file = s:Relative(':0:')
4548 let spec = s:Generate(file)
4549 if spec =~# '^fugitive:' && empty(s:DirCommitFile(spec)[2])
4550 let spec = FugitiveVimPath(spec . s:Relative('/'))
4553 let restore = s:diff_restore()
4554 let w:fugitive_diff_restore = restore
4555 if len(spec) && s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
4556 let mods = s:Mods(mods, 'rightbelow')
4558 let mods = s:Mods(mods, 'leftabove')
4560 let mods = (a:autodir ? s:diff_modifier(2) : '') . mods
4561 if &diffopt =~# 'vertical'
4562 let diffopt = &diffopt
4563 set diffopt-=vertical
4565 execute mods 'diffsplit' s:fnameescape(spec)
4566 let &l:readonly = &l:readonly
4568 let w:fugitive_diff_restore = restore
4570 if getwinvar('#', '&diff')
4577 return 'echoerr ' . string(v:exception)
4579 if exists('diffopt')
4580 let &diffopt = diffopt
4585 " Section: :Gmove, :Gremove
4587 function! s:Move(force, rename, destination) abort
4590 if s:DirCommitFile(@%)[1] !~# '^0\=$' || empty(@%)
4591 return 'echoerr ' . string('fugitive: mv not supported for this buffer')
4593 if a:destination =~# '^\.\.\=\%(/\|$\)'
4594 let destination = simplify(getcwd() . '/' . a:destination)
4595 elseif a:destination =~# '^\a\+:\|^/'
4596 let destination = a:destination
4597 elseif a:destination =~# '^:/:\='
4598 let destination = s:Tree(dir) . substitute(a:destination, '^:/:\=', '', '')
4599 elseif a:destination =~# '^:(\%(top\|top,literal\|literal,top\))'
4600 let destination = s:Tree(dir) . matchstr(a:destination, ')\zs.*')
4601 elseif a:destination =~# '^:(literal)'
4602 let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
4604 let destination = expand('%:p:s?[\/]$??:h') . '/' . a:destination
4606 let destination = s:Tree(dir) . '/' . a:destination
4608 let destination = s:Slash(destination)
4612 let [message, exec_error] = s:ChompError(['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination], dir)
4614 let v:errmsg = 'fugitive: '.message
4615 return 'echoerr v:errmsg'
4617 if isdirectory(destination)
4618 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
4620 let reload = '|call fugitive#ReloadStatus(' . string(dir) . ', 1)'
4621 if empty(s:DirCommitFile(@%)[1])
4622 if isdirectory(destination)
4623 return 'keepalt edit '.s:fnameescape(destination) . reload
4625 return 'keepalt saveas! '.s:fnameescape(destination) . reload
4628 return 'file '.s:fnameescape(fugitive#Find(':0:'.destination, dir)) . reload
4632 function! fugitive#RenameComplete(A,L,P) abort
4633 if a:A =~# '^[.:]\=/'
4634 return fugitive#CompletePath(a:A)
4636 let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
4637 return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
4641 function! fugitive#MoveCommand(line1, line2, range, bang, mods, arg, args) abort
4642 return s:Move(a:bang, 0, a:arg)
4645 function! fugitive#RenameCommand(line1, line2, range, bang, mods, arg, args) abort
4646 return s:Move(a:bang, 1, a:arg)
4649 function! s:Remove(after, force) abort
4652 if len(@%) && s:DirCommitFile(@%)[1] ==# ''
4654 elseif s:DirCommitFile(@%)[1] ==# '0'
4655 let cmd = ['rm','--cached']
4657 return 'echoerr ' . string('fugitive: rm not supported for this buffer')
4660 let cmd += ['--force']
4662 let [message, exec_error] = s:ChompError(cmd + ['--', expand('%:p')], dir)
4664 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
4665 return 'echoerr '.string(v:errmsg)
4667 return a:after . (a:force ? '!' : ''). '|call fugitive#ReloadStatus(' . string(dir) . ', 1)'
4671 function! fugitive#RemoveCommand(line1, line2, range, bang, mods, arg, args) abort
4672 return s:Remove('edit', a:bang)
4675 function! fugitive#DeleteCommand(line1, line2, range, bang, mods, arg, args) abort
4676 return s:Remove('bdelete', a:bang)
4681 function! s:Keywordprg() abort
4682 let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
4683 if has('gui_running') && !has('win32')
4684 return s:UserCommand() . ' --no-pager' . args . ' log -1'
4686 return s:UserCommand() . args . ' show'
4690 function! s:linechars(pattern) abort
4691 let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
4692 if exists('*synconcealed') && &conceallevel > 1
4693 for col in range(1, chars)
4694 let chars -= synconcealed(line('.'), col)[0]
4700 function! s:BlameBufnr(...) abort
4701 let state = s:TempState(bufname(a:0 ? a:1 : ''))
4702 if get(state, 'filetype', '') ==# 'fugitiveblame'
4703 return get(state, 'bufnr', -1)
4709 function! s:BlameCommitFileLnum(...) abort
4710 let line = a:0 ? a:1 : getline('.')
4711 let state = a:0 ? a:2 : s:TempState()
4712 let commit = matchstr(line, '^\^\=\zs\x\+')
4713 if commit =~# '^0\+$'
4715 elseif line !~# '^\^' && has_key(state, 'blame_reverse_end')
4716 let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '')
4718 let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
4719 let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)')
4720 if empty(path) && lnum
4721 let path = get(state, 'blame_file', '')
4723 return [commit, path, lnum]
4726 function! s:BlameLeave() abort
4727 let bufwinnr = bufwinnr(s:BlameBufnr())
4729 let bufnr = bufnr('')
4730 exe bufwinnr . 'wincmd w'
4731 return bufnr . 'bdelete'
4736 function! s:BlameQuit() abort
4737 let cmd = s:BlameLeave()
4740 elseif len(s:DirCommitFile(@%)[1])
4741 return cmd . '|Gedit'
4747 function! fugitive#BlameComplete(A, L, P) abort
4748 return s:CompleteSub('blame', a:A, a:L, a:P)
4751 function! s:BlameSubcommand(line1, count, range, bang, mods, args) abort
4753 let flags = copy(a:args)
4759 if a:line1 > 0 && a:count > 0 && a:range != 1
4760 call extend(ranges, ['-L', a:line1 . ',' . a:count])
4762 while i < len(flags)
4763 let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
4764 if len(match) && len(match[2])
4765 call insert(flags, match[1])
4766 let flags[i+1] = '-' . match[2]
4770 if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$'
4772 elseif arg ==# '--contents' && i + 1 < len(flags)
4773 call extend(commits, remove(flags, i, i+1))
4775 elseif arg ==# '-L' && i + 1 < len(flags)
4776 call extend(ranges, remove(flags, i, i+1))
4778 elseif arg =~# '^--contents='
4779 call add(commits, remove(flags, i))
4781 elseif arg =~# '^-L.'
4782 call add(ranges, remove(flags, i))
4784 elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\|ignore-rev\|ignore-revs-file\)$'
4788 echo s:ChompError(['blame', arg])[0]
4793 if i + 1 < len(flags)
4794 call extend(files, remove(flags, i + 1, -1))
4796 call remove(flags, i)
4798 elseif arg !~# '^-' && (s:HasOpt(flags, '--not') || arg !~# '^\^')
4799 if index(flags, '--') >= 0
4800 call add(commits, remove(flags, i))
4803 if arg =~# '\.\.' && arg !~# '^\.\.\=\%(/\|$\)' && empty(commits)
4804 call add(commits, remove(flags, i))
4808 let dcf = s:DirCommitFile(fugitive#Find(arg))
4809 if len(dcf[1]) && empty(dcf[2])
4810 call add(commits, remove(flags, i))
4815 call add(files, remove(flags, i))
4820 let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./'))), '^\.\%(/\|$\)', '', '')
4821 if empty(commits) && len(files) > 1
4822 call add(commits, remove(files, 1))
4826 let cmd = ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', 'blame', '--show-number']
4827 call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
4828 if a:count > 0 && empty(ranges)
4829 let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
4831 call extend(cmd, ranges)
4834 elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$'))
4835 let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')]
4836 elseif empty(files) && !s:HasOpt(flags, '--reverse')
4837 let cmd += ['--contents', '-']
4839 let basecmd = escape(fugitive#Prepare(cmd) . ' -- ' . s:shellesc(len(files) ? files : file), '!#%')
4840 let tempname = tempname()
4841 let error = tempname . '.err'
4842 let temp = tempname . (raw ? '' : '.fugitiveblame')
4844 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
4846 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
4848 let l:shell_error = v:shell_error
4852 let lines = readfile(error)
4854 let lines = readfile(temp)
4856 for i in range(len(lines))
4857 if lines[i] =~# '^error: \|^fatal: '
4865 if i != len(lines) - 1
4871 let temp_state = {'dir': s:Dir(), 'filetype': (raw ? '' : 'fugitiveblame'), 'blame_flags': flags, 'blame_file': file, 'modifiable': 0}
4872 if s:HasOpt(flags, '--reverse')
4873 let temp_state.blame_reverse_end = matchstr(get(commits, 0, ''), '\.\.\zs.*')
4875 if (a:line1 == 0 || a:range == 1) && a:count > 0
4876 let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split')
4877 return s:BlameCommit(edit, get(readfile(temp), 0, ''), temp_state)
4879 let temp = s:Resolve(temp)
4880 let s:temp_files[s:cpath(temp)] = temp_state
4881 if len(ranges + commits + files) || raw
4882 let mods = s:Mods(a:mods)
4884 exe 'silent keepalt' mods 'split' s:fnameescape(temp)
4885 elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden)
4886 exe 'silent' mods 'edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp)
4888 return mods . 'edit ' . s:fnameescape(temp)
4892 if a:mods =~# '\<tab\>'
4895 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4896 for winnr in range(winnr('$'),1,-1)
4897 if getwinvar(winnr, '&scrollbind')
4898 call setwinvar(winnr, '&scrollbind', 0)
4900 if exists('+cursorbind') && getwinvar(winnr, '&cursorbind')
4901 call setwinvar(winnr, '&cursorbind', 0)
4903 if s:BlameBufnr(winbufnr(winnr)) > 0
4904 execute winbufnr(winnr).'bdelete'
4907 let bufnr = bufnr('')
4908 let temp_state.bufnr = bufnr
4909 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
4910 if exists('+cursorbind')
4911 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
4914 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
4917 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
4919 setlocal scrollbind nowrap nofoldenable
4920 if exists('+cursorbind')
4923 let top = line('w0') + &scrolloff
4924 let current = line('.')
4925 exe 'silent keepalt' (a:bang ? s:Mods(mods) . 'split' : s:Mods(mods, 'leftabove') . 'vsplit') s:fnameescape(temp)
4926 let w:fugitive_leave = restore
4930 if exists('+cursorbind')
4933 setlocal nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth
4934 if exists('+relativenumber')
4935 setlocal norelativenumber
4937 execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
4938 call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>')
4939 call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>')
4940 call s:Map('n', 'D', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze\\d\\ze\\s\\+\\d\\+)')+1-v:count)<CR>", '<silent>')
4947 return 'echoerr ' . string(v:exception)
4951 function! s:BlameCommit(cmd, ...) abort
4952 let line = a:0 ? a:1 : getline('.')
4953 let state = a:0 ? a:2 : s:TempState()
4954 let sigil = has_key(state, 'blame_reverse_end') ? '-' : '+'
4955 let mods = (s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ")
4956 let [commit, path, lnum] = s:BlameCommitFileLnum(line, state)
4957 if empty(commit) && len(path) && has_key(state, 'blame_reverse_end')
4958 let path = (len(state.blame_reverse_end) ? state.blame_reverse_end . ':' : ':(top)') . path
4959 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4961 if commit =~# '^0*$'
4962 return 'echoerr ' . string('fugitive: no commit')
4964 if line =~# '^\^' && !has_key(state, 'blame_reverse_end')
4965 let path = commit . ':' . path
4966 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4968 let cmd = fugitive#Open(mods . a:cmd, 0, '', commit, [commit])
4969 if cmd =~# '^echoerr'
4973 if a:cmd ==# 'pedit' || empty(path)
4976 if search('^diff .* b/\M'.escape(path,'\').'$','W')
4978 let head = line('.')
4979 while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
4980 let top = +matchstr(getline('.'),' ' . sigil .'\zs\d\+')
4981 let len = +matchstr(getline('.'),' ' . sigil . '\d\+,\zs\d\+')
4982 if lnum >= top && lnum <= top + len
4983 let offset = lnum - top
4991 while offset > 0 && line('.') < line('$')
4993 if getline('.') =~# '^[ ' . sigil . ']'
5006 function! s:BlameJump(suffix, ...) abort
5007 let suffix = a:suffix
5008 let [commit, path, lnum] = s:BlameCommitFileLnum()
5010 return 'echoerr ' . string('fugitive: could not determine filename for blame')
5012 if commit =~# '^0*$'
5016 let offset = line('.') - line('w0')
5017 let flags = get(s:TempState(), 'blame_flags', [])
5019 if s:HasOpt(flags, '--reverse')
5020 call remove(flags, '--reverse')
5022 call add(flags, '--reverse')
5025 let blame_bufnr = s:BlameBufnr()
5027 let bufnr = bufnr('')
5028 let winnr = bufwinnr(blame_bufnr)
5030 exe winnr.'wincmd w'
5032 execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
5038 if exists(':Gblame')
5039 let my_bufnr = bufnr('')
5041 let blame_args = flags + [commit . suffix, '--', path]
5042 let result = s:BlameSubcommand(0, 0, 0, 0, '', blame_args)
5044 let blame_args = flags
5045 let result = s:BlameSubcommand(-1, -1, 0, 0, '', blame_args)
5047 if bufnr('') == my_bufnr
5052 let delta = line('.') - line('w0') - offset
5054 execute 'normal! '.delta."\<C-E>"
5056 execute 'normal! '.(-delta)."\<C-Y>"
5060 echo ':Gblame' s:fnameescape(blame_args)
5065 let s:hash_colors = {}
5067 function! fugitive#BlameSyntax() abort
5068 let conceal = has('conceal') ? ' conceal' : ''
5069 let config = fugitive#Config()
5070 let flags = get(s:TempState(), 'blame_flags', [])
5071 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
5072 syn match FugitiveblameHash "\%(^\^\=[?*]*\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5073 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5074 if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$' || s:HasOpt(flags, '-b')
5075 syn match FugitiveblameBoundaryIgnore "^\^[*?]*\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5077 syn match FugitiveblameBoundary "^\^"
5079 syn match FugitiveblameScoreDebug " *\d\+\s\+\d\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile contained skipwhite
5080 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
5081 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
5082 exec 'syn match FugitiveblameLineNumber "\s*\d\+)\@=" contained containedin=FugitiveblameAnnotation' conceal
5083 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)
5084 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5085 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5086 syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
5087 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
5088 hi def link FugitiveblameBoundary Keyword
5089 hi def link FugitiveblameHash Identifier
5090 hi def link FugitiveblameBoundaryIgnore Ignore
5091 hi def link FugitiveblameUncommitted Ignore
5092 hi def link FugitiveblameScoreDebug Debug
5093 hi def link FugitiveblameTime PreProc
5094 hi def link FugitiveblameLineNumber Number
5095 hi def link FugitiveblameOriginalFile String
5096 hi def link FugitiveblameOriginalLineNumber Float
5097 hi def link FugitiveblameShort FugitiveblameDelimiter
5098 hi def link FugitiveblameDelimiter Delimiter
5099 hi def link FugitiveblameNotCommittedYet Comment
5100 if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
5104 for lnum in range(1, line('$'))
5105 let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
5106 if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
5110 if &t_Co > 16 && get(g:, 'CSApprox_loaded') && !empty(findfile('autoload/csapprox/per_component.vim', escape(&rtp, ' ')))
5111 \ && empty(get(s:hash_colors, hash))
5112 let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
5113 let color = csapprox#per_component#Approximate(r, g, b)
5114 if color == 16 && &background ==# 'dark'
5117 let s:hash_colors[hash] = ' ctermfg='.color
5119 let s:hash_colors[hash] = ''
5121 exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
5123 call s:BlameRehighlight()
5126 function! s:BlameRehighlight() abort
5127 for [hash, cterm] in items(s:hash_colors)
5128 if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
5129 exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
5131 exe 'hi link FugitiveblameHash'.hash.' Identifier'
5136 function! s:BlameFileType() abort
5138 setlocal foldmethod=manual
5140 let &l:keywordprg = s:Keywordprg()
5142 let b:undo_ftplugin = 'setl keywordprg= foldmethod<'
5143 if exists('+concealcursor')
5144 setlocal concealcursor=nc conceallevel=2
5145 let b:undo_ftplugin .= ' concealcursor< conceallevel<'
5150 call s:Map('n', '<F1>', ':help :Gblame<CR>', '<silent>')
5151 call s:Map('n', 'g?', ':help :Gblame<CR>', '<silent>')
5152 if mapcheck('q', 'n') =~# '^$\|bdelete'
5153 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>')
5155 call s:Map('n', 'gq', ':exe <SID>BlameQuit()<CR>', '<silent>')
5156 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5157 call s:Map('n', '<CR>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5158 call s:Map('n', '-', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>')
5159 call s:Map('n', 'P', ':<C-U>exe <SID>BlameJump("^".v:count1)<CR>', '<silent>')
5160 call s:Map('n', '~', ':<C-U>exe <SID>BlameJump("~".v:count1)<CR>', '<silent>')
5161 call s:Map('n', 'i', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5162 call s:Map('n', 'o', ':<C-U>exe <SID>BlameCommit("split")<CR>', '<silent>')
5163 call s:Map('n', 'O', ':<C-U>exe <SID>BlameCommit("tabedit")<CR>', '<silent>')
5164 call s:Map('n', 'p', ':<C-U>exe <SID>BlameCommit("pedit")<CR>', '<silent>')
5167 augroup fugitive_blame
5169 autocmd FileType fugitiveblame call s:BlameFileType()
5170 autocmd ColorScheme,GUIEnter * call s:BlameRehighlight()
5171 autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
5176 let s:redirects = {}
5178 function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, args) abort
5182 let validremote = '\.\|\.\=/.*\|[[:alnum:]_-]\+\%(://.\{-\}\)\='
5185 return 'echoerr ' . string('fugitive: ''-'' no longer required to get persistent URL if range given')
5187 return 'echoerr ' . string('fugitive: use :0Gbrowse instead of :Gbrowse -')
5190 let remote = matchstr(join(a:args, ' '),'@\zs\%('.validremote.'\)$')
5191 let rev = substitute(join(a:args, ' '),'@\%('.validremote.'\)$','','')
5197 let rev = s:DirRev(@%)[1]
5200 let expanded = s:Relative()
5202 let expanded = s:Expand(rev)
5204 let cdir = FugitiveVimPath(fugitive#CommonDir(dir))
5205 for subdir in ['tags/', 'heads/', 'remotes/']
5206 if expanded !~# '^[./]' && filereadable(cdir . '/refs/' . subdir . expanded)
5207 let expanded = '.git/refs/' . subdir . expanded
5210 let full = fugitive#Find(expanded, dir)
5212 if full =~? '^fugitive:'
5213 let [pathdir, commit, path] = s:DirCommitFile(full)
5214 if commit =~# '^:\=\d$'
5218 let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':'))
5219 let branch = matchstr(expanded, '^[^:]*')
5223 let path = path[1:-1]
5224 elseif empty(s:Tree(dir))
5225 let path = '.git/' . full[strlen(dir)+1:-1]
5228 let path = fugitive#Path(full, '/')[1:-1]
5229 if path =~# '^\.git/'
5231 elseif isdirectory(full) || empty(path)
5237 if type ==# 'tree' && !empty(path)
5238 let path = s:sub(path, '/\=$', '/')
5240 if path =~# '^\.git/.*HEAD$' && filereadable(dir . '/' . path[5:-1])
5241 let body = readfile(dir . '/' . path[5:-1])[0]
5242 if body =~# '^\x\{40,\}$'
5246 elseif body =~# '^ref: refs/'
5247 let path = '.git/' . matchstr(body,'ref: \zs.*')
5252 if path =~# '^\.git/refs/remotes/.'
5254 let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+')
5255 let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5257 let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5258 let path = '.git/refs/heads/'.merge
5260 elseif path =~# '^\.git/refs/heads/.'
5261 let branch = path[16:-1]
5262 elseif !exists('branch')
5263 let branch = FugitiveHead()
5266 let r = fugitive#Config('branch.'.branch.'.remote')
5267 let m = fugitive#Config('branch.'.branch.'.merge')[11:-1]
5268 if r ==# '.' && !empty(m)
5269 let r2 = fugitive#Config('branch.'.m.'.remote')
5272 let m = fugitive#Config('branch.'.m.'.merge')[11:-1]
5278 if r ==# '.' || r ==# remote
5280 if path =~# '^\.git/refs/heads/.'
5281 let path = '.git/refs/heads/'.merge
5286 let line1 = a:count > 0 ? a:line1 : 0
5287 let line2 = a:count > 0 ? a:count : 0
5288 if empty(commit) && path !~# '^\.git/'
5289 if a:count < 0 && !empty(merge)
5294 let owner = s:Owner(@%)
5295 let [commit, exec_error] = s:ChompError(['merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? 'HEAD' : owner, '--'])
5299 if a:count > 0 && empty(a:args) && commit =~# '^\x\{40,\}$'
5300 let blame_list = tempname()
5301 call writefile([commit, ''], blame_list, 'b')
5302 let blame_in = tempname()
5303 silent exe '%write' blame_in
5304 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])
5306 let blame_regex = '^\^\x\+\s\+\zs\d\+\ze\s'
5307 if get(blame, 0) =~# blame_regex && get(blame, -1) =~# blame_regex
5308 let line1 = +matchstr(blame[0], blame_regex)
5309 let line2 = +matchstr(blame[-1], blame_regex)
5311 call s:throw("Can't browse to uncommitted change")
5318 let commit = readfile(fugitive#Find('.git/HEAD', dir), '', 1)[0]
5321 while commit =~# '^ref: ' && i < 10
5322 let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
5330 let raw = fugitive#RemoteUrl(remote)
5335 if raw =~# '^https\=://' && s:executable('curl')
5336 if !has_key(s:redirects, raw)
5337 let s:redirects[raw] = matchstr(system('curl -I ' .
5338 \ s:shellesc(raw . '/info/refs?service=git-upload-pack')),
5339 \ 'Location: \zs\S\+\ze/info/refs?')
5341 if len(s:redirects[raw])
5342 let raw = s:redirects[raw]
5348 \ 'repo': fugitive#repo(dir),
5350 \ 'revision': 'No longer provided',
5358 for Handler in get(g:, 'fugitive_browse_handlers', [])
5359 let url = call(Handler, [copy(opts)])
5366 call s:throw("No Gbrowse handler installed for '".raw."'")
5369 let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))')
5374 return 'echomsg '.string(url)
5375 elseif exists(':Browse') == 2
5376 return 'echomsg '.string(url).'|Browse '.url
5378 if !exists('g:loaded_netrw')
5379 runtime! autoload/netrw.vim
5381 if exists('*netrw#BrowseX')
5382 return 'echomsg '.string(url).'|call netrw#BrowseX('.string(url).', 0)'
5384 return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
5388 return 'echoerr ' . string(v:exception)
5392 " Section: Go to file
5394 nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
5395 function! fugitive#MapCfile(...) abort
5396 exe 'cnoremap <buffer> <expr> <Plug><cfile>' (a:0 ? a:1 : 'fugitive#Cfile()')
5397 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap <buffer> <Plug><cfile>"'
5398 if !exists('g:fugitive_no_maps')
5399 call s:Map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>', 1)
5400 call s:Map('n', '<C-W>f', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5401 call s:Map('n', '<C-W><C-F>', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5402 call s:Map('n', '<C-W>gf', '<SID>:tabfind <Plug><cfile><CR>', '<silent><unique>', 1)
5403 call s:Map('c', '<C-R><C-F>', '<Plug><cfile>', '<silent><unique>', 1)
5407 function! s:ContainingCommit() abort
5408 let commit = s:Owner(@%)
5409 return empty(commit) ? 'HEAD' : commit
5412 function! s:SquashArgument(...) abort
5413 if &filetype == 'fugitive'
5414 let commit = matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze ')
5415 elseif has_key(s:temp_files, s:cpath(expand('%:p')))
5416 let commit = matchstr(getline('.'), '\<\x\{4,\}\>')
5418 let commit = s:Owner(@%)
5420 return len(commit) && a:0 ? printf(a:1, commit) : commit
5423 function! s:RebaseArgument() abort
5424 return s:SquashArgument(' %s^')
5427 function! s:NavigateUp(count) abort
5428 let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
5432 let rev = matchstr(rev, '.*\ze/.\+', '')
5433 elseif rev =~# '.:.'
5434 let rev = matchstr(rev, '^.[^:]*:')
5447 function! s:MapMotion(lhs, rhs) abort
5448 call s:Map('n', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5449 call s:Map('o', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5450 call s:Map('x', a:lhs, ":<C-U>exe 'normal! gv'<Bar>" . a:rhs . "<CR>", "<silent>")
5453 function! fugitive#MapJumps(...) abort
5455 if get(b:, 'fugitive_type', '') ==# 'blob'
5456 let blame_map = 'Gblame<C-R>=v:count ? " --reverse" : ""<CR><CR>'
5457 call s:Map('n', '<2-LeftMouse>', ':<C-U>0,1' . blame_map, '<silent>')
5458 call s:Map('n', '<CR>', ':<C-U>0,1' . blame_map, '<silent>')
5459 call s:Map('n', 'o', ':<C-U>0,2' . blame_map, '<silent>')
5460 call s:Map('n', 'p', ':<C-U>0,3' . blame_map, '<silent>')
5461 call s:Map('n', 'gO', ':<C-U>0,4' . blame_map, '<silent>')
5462 call s:Map('n', 'O', ':<C-U>0,5' . blame_map, '<silent>')
5464 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>')
5465 call s:Map('n', 'dd', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<CR>", '<silent>')
5466 call s:Map('n', 'dh', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5467 call s:Map('n', 'ds', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5468 call s:Map('n', 'dv', ":<C-U>call <SID>DiffClose()<Bar>Gvdiffsplit!<CR>", '<silent>')
5469 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
5472 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5473 call s:Map('n', '<CR>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5474 call s:Map('n', 'o', ':<C-U>exe <SID>GF("split")<CR>', '<silent>')
5475 call s:Map('n', 'gO', ':<C-U>exe <SID>GF("vsplit")<CR>', '<silent>')
5476 call s:Map('n', 'O', ':<C-U>exe <SID>GF("tabedit")<CR>', '<silent>')
5477 call s:Map('n', 'p', ':<C-U>exe <SID>GF("pedit")<CR>', '<silent>')
5479 if !exists('g:fugitive_no_maps')
5480 if exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^<c-p>$'
5481 nnoremap <buffer> <silent> <C-P> :<C-U>execute line('.') == 1 ? 'CtrlP ' . fnameescape(<SID>Tree()) : <SID>PreviousItem(v:count1)<CR>
5483 nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>PreviousItem(v:count1)<CR>
5485 nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>NextItem(v:count1)<CR>
5487 call s:MapMotion('(', 'exe <SID>PreviousItem(v:count1)')
5488 call s:MapMotion(')', 'exe <SID>NextItem(v:count1)')
5489 call s:MapMotion('K', 'exe <SID>PreviousHunk(v:count1)')
5490 call s:MapMotion('J', 'exe <SID>NextHunk(v:count1)')
5491 call s:MapMotion('[c', 'exe <SID>PreviousHunk(v:count1)')
5492 call s:MapMotion(']c', 'exe <SID>NextHunk(v:count1)')
5493 call s:MapMotion('[/', 'exe <SID>PreviousFile(v:count1)')
5494 call s:MapMotion(']/', 'exe <SID>NextFile(v:count1)')
5495 call s:MapMotion('[m', 'exe <SID>PreviousFile(v:count1)')
5496 call s:MapMotion(']m', 'exe <SID>NextFile(v:count1)')
5497 call s:MapMotion('[[', 'exe <SID>PreviousSection(v:count1)')
5498 call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
5499 call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
5500 call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
5501 call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
5502 call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
5504 call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent>')
5505 call s:Map('n', 'dq', ":<C-U>call <SID>DiffClose()<CR>", '<silent>')
5506 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>')
5507 call s:Map('n', 'P', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5508 call s:Map('n', '~', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5509 call s:Map('n', 'C', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5510 call s:Map('n', 'cp', ":<C-U>echoerr 'Use gC'<CR>", '<silent>')
5511 call s:Map('n', 'gC', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5512 call s:Map('n', 'gc', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5513 call s:Map('n', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5514 call s:Map('x', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5516 nnoremap <buffer> c<Space> :Git commit<Space>
5517 nnoremap <buffer> c<CR> :Git commit<CR>
5518 nnoremap <buffer> cv<Space> :Git commit -v<Space>
5519 nnoremap <buffer> cv<CR> :Git commit -v<CR>
5520 nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
5521 nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
5522 nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
5523 nnoremap <buffer> <silent> cw :<C-U>Gcommit --amend --only<CR>
5524 nnoremap <buffer> <silent> cva :<C-U>Gcommit -v --amend<CR>
5525 nnoremap <buffer> <silent> cvc :<C-U>Gcommit -v<CR>
5526 nnoremap <buffer> <silent> cRa :<C-U>Gcommit --reset-author --amend<CR>
5527 nnoremap <buffer> <silent> cRe :<C-U>Gcommit --reset-author --amend --no-edit<CR>
5528 nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR>
5529 nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5530 nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5531 nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5532 nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5533 nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
5534 nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR>
5536 nnoremap <buffer> cr<Space> :Git revert<Space>
5537 nnoremap <buffer> cr<CR> :Git revert<CR>
5538 nnoremap <buffer> <silent> crc :<C-U>Grevert <C-R>=<SID>SquashArgument()<CR><CR>
5539 nnoremap <buffer> <silent> crn :<C-U>Grevert --no-commit <C-R>=<SID>SquashArgument()<CR><CR>
5540 nnoremap <buffer> <silent> cr? :help fugitive_cr<CR>
5542 nnoremap <buffer> cm<Space> :Git merge<Space>
5543 nnoremap <buffer> cm<CR> :Git merge<CR>
5544 nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
5546 nnoremap <buffer> cz<Space> :Git stash<Space>
5547 nnoremap <buffer> cz<CR> :Git stash<CR>
5548 nnoremap <buffer> <silent> cza :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5549 nnoremap <buffer> <silent> czA :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', 'stash@{' . v:count . '}'])<CR>
5550 nnoremap <buffer> <silent> czp :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5551 nnoremap <buffer> <silent> czP :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', 'stash@{' . v:count . '}'])<CR>
5552 nnoremap <buffer> <silent> czv :<C-U>exe 'Gedit' fugitive#RevParse('stash@{' . v:count . '}')<CR>
5553 nnoremap <buffer> <silent> czw :<C-U>exe <SID>EchoExec(['stash', '--keep-index'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5554 nnoremap <buffer> <silent> czz :<C-U>exe <SID>EchoExec(['stash'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5555 nnoremap <buffer> <silent> cz? :<C-U>help fugitive_cz<CR>
5557 nnoremap <buffer> co<Space> :Git checkout<Space>
5558 nnoremap <buffer> co<CR> :Git checkout<CR>
5559 nnoremap <buffer> coo :exe <SID>EchoExec(['checkout'] + split(<SID>SquashArgument()) + ['--'])<CR>
5560 nnoremap <buffer> co? :<C-U>help fugitive_co<CR>
5562 nnoremap <buffer> cb<Space> :Git branch<Space>
5563 nnoremap <buffer> cb<CR> :Git branch<CR>
5564 nnoremap <buffer> cb? :<C-U>help fugitive_cb<CR>
5566 nnoremap <buffer> r<Space> :Git rebase<Space>
5567 nnoremap <buffer> r<CR> :Git rebase<CR>
5568 nnoremap <buffer> <silent> ri :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><CR>
5569 nnoremap <buffer> <silent> rf :<C-U>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><CR>
5570 nnoremap <buffer> <silent> ru :<C-U>Grebase --interactive @{upstream}<CR>
5571 nnoremap <buffer> <silent> rp :<C-U>Grebase --interactive @{push}<CR>
5572 nnoremap <buffer> <silent> rw :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/reword/e<CR>
5573 nnoremap <buffer> <silent> rm :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/edit/e<CR>
5574 nnoremap <buffer> <silent> rd :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5575 nnoremap <buffer> <silent> rk :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5576 nnoremap <buffer> <silent> rx :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5577 nnoremap <buffer> <silent> rr :<C-U>Grebase --continue<CR>
5578 nnoremap <buffer> <silent> rs :<C-U>Grebase --skip<CR>
5579 nnoremap <buffer> <silent> re :<C-U>Grebase --edit-todo<CR>
5580 nnoremap <buffer> <silent> ra :<C-U>Grebase --abort<CR>
5581 nnoremap <buffer> <silent> r? :<C-U>help fugitive_r<CR>
5583 call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5584 call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5585 call s:Map('n', 'g?', ":<C-U>help fugitive-map<CR>", '<silent>')
5586 call s:Map('n', '<F1>', ":<C-U>help fugitive-map<CR>", '<silent>')
5590 function! s:StatusCfile(...) abort
5592 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5593 let info = s:StageInfo()
5594 let line = getline('.')
5595 if len(info.sigil) && len(info.section) && len(info.paths)
5596 if info.section ==# 'Unstaged' && info.sigil !=# '-'
5597 return [lead . info.relative[0], info.offset, 'normal!zv']
5598 elseif info.section ==# 'Staged' && info.sigil ==# '-'
5599 return ['@:' . info.relative[0], info.offset, 'normal!zv']
5601 return [':0:' . info.relative[0], info.offset, 'normal!zv']
5603 elseif len(info.paths)
5604 return [lead . info.relative[0]]
5605 elseif len(info.commit)
5606 return [info.commit]
5607 elseif line =~# '^\%(Head\|Merge\|Rebase\|Upstream\|Pull\|Push\): '
5608 return [matchstr(line, ' \zs.*')]
5614 function! fugitive#StatusCfile() abort
5615 let file = s:Generate(s:StatusCfile()[0])
5616 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5619 function! s:MessageCfile(...) abort
5621 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5622 if getline('.') =~# '^.\=\trenamed:.* -> '
5623 return lead . matchstr(getline('.'),' -> \zs.*')
5624 elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
5625 return lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
5626 elseif getline('.') =~# '^.\=\t.'
5627 return lead . matchstr(getline('.'),'\t\zs.*')
5628 elseif getline('.') =~# ': needs merge$'
5629 return lead . matchstr(getline('.'),'.*\ze: needs merge$')
5630 elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
5632 elseif getline('.') =~# '^\%(. \)\=On branch '
5633 return 'refs/heads/'.getline('.')[12:]
5634 elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
5635 return matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
5641 function! fugitive#MessageCfile() abort
5642 let file = s:Generate(s:MessageCfile())
5643 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5646 function! s:cfile() abort
5648 let myhash = s:DirRev(@%)[1]
5651 let myhash = fugitive#RevParse(myhash)
5656 if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w'
5657 let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
5660 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
5662 let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
5663 \ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
5665 if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40,\}\t'
5666 return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
5668 return [treebase . s:sub(getline('.'),'/$','')]
5675 if getline('.') =~# '^\d\{6\} \x\{40,\} \d\t'
5676 let ref = matchstr(getline('.'),'\x\{40,\}')
5677 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
5681 if getline('.') =~# '^ref: '
5682 let ref = strpart(getline('.'),5)
5684 elseif getline('.') =~# '^commit \x\{40,\}\>'
5685 let ref = matchstr(getline('.'),'\x\{40,\}')
5688 elseif getline('.') =~# '^parent \x\{40,\}\>'
5689 let ref = matchstr(getline('.'),'\x\{40,\}')
5690 let line = line('.')
5692 while getline(line) =~# '^parent '
5698 elseif getline('.') =~# '^tree \x\{40,\}$'
5699 let ref = matchstr(getline('.'),'\x\{40,\}')
5700 if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
5701 let ref = myhash.':'
5705 elseif getline('.') =~# '^object \x\{40,\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
5706 let ref = matchstr(getline('.'),'\x\{40,\}')
5707 let type = matchstr(getline(line('.')+1),'type \zs.*')
5709 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
5710 let ref = s:DirRev(@%)[1]
5712 elseif getline('.') =~# '^\l\{3,8\} \x\{40,\}\>'
5713 let ref = matchstr(getline('.'),'\x\{40,\}')
5714 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
5716 elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
5717 let ref = getline('.')[4:]
5719 elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
5720 let type = getline('.')[0]
5721 let lnum = line('.') - 1
5723 while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5724 if getline(lnum) =~# '^[ '.type.']'
5729 let offset += matchstr(getline(lnum), type.'\zs\d\+')
5730 let ref = getline(search('^'.type.'\{3\} [abciow12]/','bnW'))[4:-1]
5731 let dcmds = [offset, 'normal!zv']
5733 elseif getline('.') =~# '^rename from '
5734 let ref = 'a/'.getline('.')[12:]
5735 elseif getline('.') =~# '^rename to '
5736 let ref = 'b/'.getline('.')[10:]
5738 elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5739 let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
5740 let offset = matchstr(getline('.'), '+\zs\d\+')
5742 let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5743 let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5744 let dcmd = 'Gdiffsplit! +'.offset
5746 elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5747 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5748 let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5749 let dcmd = 'Gdiffsplit!'
5751 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5752 let line = getline(line('.')-1)
5753 let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5754 let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5755 let dcmd = 'Gdiffsplit!'
5757 elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
5758 let ref = getline('.')
5760 elseif expand('<cword>') =~# '^\x\{7,\}\>'
5761 return [expand('<cword>')]
5776 let prefixes.a = myhash.'^:'
5777 let prefixes.b = myhash.':'
5779 let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5781 let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5784 if ref ==# '/dev/null'
5786 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
5790 return [ref, dcmd . ' ' . s:fnameescape(dref)] + dcmds
5792 return [ref] + dcmds
5800 function! s:GF(mode) abort
5802 let results = &filetype ==# 'fugitive' ? s:StatusCfile() : &filetype ==# 'gitcommit' ? [s:MessageCfile()] : s:cfile()
5804 return 'echoerr ' . string(v:exception)
5807 return 'G' . a:mode .
5808 \ ' +' . escape(results[1], ' ') . ' ' .
5809 \ s:fnameescape(results[0]) . join(map(results[2:-1], '"|" . v:val'), '')
5810 elseif len(results) && len(results[0])
5811 return 'G' . a:mode . ' ' . s:fnameescape(results[0])
5817 function! fugitive#Cfile() abort
5819 let results = s:cfile()
5821 let cfile = expand('<cfile>')
5822 if &includeexpr =~# '\<v:fname\>'
5823 sandbox let cfile = eval(substitute(&includeexpr, '\C\<v:fname\>', '\=string(cfile)', 'g'))
5826 elseif len(results) > 1
5827 let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' '
5829 return pre . s:fnameescape(s:Generate(results[0]))
5832 " Section: Statusline
5834 function! fugitive#Statusline(...) abort
5835 let dir = s:Dir(bufnr(''))
5840 let commit = s:DirCommitFile(@%)[1]
5842 let status .= ':' . commit[0:6]
5844 let status .= '('.FugitiveHead(7, dir).')'
5845 return '[Git'.status.']'
5848 function! fugitive#statusline(...) abort
5849 return fugitive#Statusline()
5852 function! fugitive#head(...) abort
5857 return fugitive#Head(a:0 ? a:1 : 0)
5862 function! fugitive#Foldtext() abort
5863 if &foldmethod !=# 'syntax'
5867 let line_foldstart = getline(v:foldstart)
5868 if line_foldstart =~# '^diff '
5869 let [add, remove] = [-1, -1]
5871 for lnum in range(v:foldstart, v:foldend)
5872 let line = getline(lnum)
5873 if filename ==# '' && line =~# '^[+-]\{3\} [abciow12]/'
5874 let filename = line[6:-1]
5878 elseif line =~# '^-'
5880 elseif line =~# '^Binary '
5885 let filename = matchstr(line_foldstart, '^diff .\{-\} [abciow12]/\zs.*\ze [abciow12]/')
5888 let filename = line_foldstart[5:-1]
5891 return 'Binary: '.filename
5893 return (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
5895 elseif line_foldstart =~# '^# .*:$'
5896 let lines = getline(v:foldstart, v:foldend)
5897 call filter(lines, 'v:val =~# "^#\t"')
5898 cal map(lines, "s:sub(v:val, '^#\t%(modified: +|renamed: +)=', '')")
5899 cal map(lines, "s:sub(v:val, '^([[:alpha:] ]+): +(.*)', '\\2 (\\1)')")
5900 return line_foldstart.' '.join(lines, ', ')
5905 function! fugitive#foldtext() abort
5906 return fugitive#Foldtext()
5909 augroup fugitive_folding
5911 autocmd User Fugitive
5912 \ if &filetype =~# '^git\%(commit\)\=$' && &foldtext ==# 'foldtext()' |
5913 \ set foldtext=fugitive#Foldtext() |
5917 " Section: Initialization
5919 function! fugitive#Init() abort
5920 if exists('#User#FugitiveBoot')
5921 exe s:DoAutocmd('User FugitiveBoot')
5924 if &tags !~# '\.git' && @% !~# '\.git' && !exists('s:tags_warning')
5925 let actualdir = fugitive#Find('.git/', dir)
5926 if filereadable(actualdir . 'tags')
5927 let s:tags_warning = 1
5929 echo "Fugitive .git/tags support removed in favor of `:set tags^=./.git/tags;`"
5933 exe s:DoAutocmd('User Fugitive')
5936 function! fugitive#is_git_dir(path) abort
5937 return FugitiveIsGitDir(a:path)
5940 function! fugitive#extract_git_dir(path) abort
5941 return FugitiveExtractGitDir(a:path)
5944 function! fugitive#detect(path) abort
5945 return FugitiveDetect(a:path)