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())
77 return 'return ' . string('echoerr "fugitive: not a Git repository"')
82 function! s:Mods(mods, ...) abort
83 let mods = substitute(a:mods, '\C<mods>', '', '')
84 let mods = mods =~# '\S$' ? mods . ' ' : mods
85 if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>'
86 let mods = a:1 . ' ' . mods
88 return substitute(mods, '\s\+', ' ', 'g')
91 function! s:Slash(path) abort
92 if exists('+shellslash')
93 return tr(a:path, '\', '/')
99 function! s:Resolve(path) abort
100 let path = resolve(a:path)
102 let path = FugitiveVimPath(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t'))
107 function! s:cpath(path, ...) abort
108 if exists('+fileignorecase') && &fileignorecase
109 let path = FugitiveVimPath(tolower(a:path))
111 let path = FugitiveVimPath(a:path)
113 return a:0 ? path ==# s:cpath(a:1) : path
116 function! s:Cd(...) abort
117 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'
125 exe cd s:fnameescape(a:1)
126 return cd . ' ' . s:fnameescape(cwd)
129 let s:executables = {}
131 function! s:executable(binary) abort
132 if !has_key(s:executables, a:binary)
133 let s:executables[a:binary] = executable(a:binary)
135 return s:executables[a:binary]
138 let s:nowait = v:version >= 704 ? '<nowait>' : ''
140 function! s:Map(mode, lhs, rhs, ...) abort
141 for mode in split(a:mode, '\zs')
142 let flags = (a:0 ? a:1 : '') . (a:rhs =~# '<Plug>' ? '' : '<script>')
145 let keys = get(g:, mode.'remap', {})
146 if type(keys) == type([])
150 if has_key(keys, head)
151 let head = keys[head]
157 let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
158 let head = substitute(head, '<[^<>]*>$\|.$', '', '')
160 if flags !~# '<unique>' || empty(mapcheck(head.tail, mode))
161 exe mode.'map <buffer>' s:nowait flags head.tail a:rhs
163 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') .
164 \ '|sil! exe "' . mode . 'unmap <buffer> ' . head.tail . '"'
172 function! s:QuickfixGet(nr, ...) abort
174 return call('getqflist', a:000)
176 return call('getloclist', [a:nr] + a:000)
180 function! s:QuickfixSet(nr, ...) abort
182 return call('setqflist', a:000)
184 return call('setloclist', [a:nr] + a:000)
188 function! s:QuickfixCreate(nr, opts) abort
189 if has('patch-7.4.2200')
190 call s:QuickfixSet(a:nr, [], ' ', a:opts)
192 call s:QuickfixSet(a:nr, [], ' ')
196 function! s:QuickfixStream(nr, title, cmd, first, callback, ...) abort
197 call s:QuickfixCreate(a:nr, {'title': a:title})
199 exe a:nr < 0 ? 'copen' : 'lopen'
205 let lines = split(s:SystemError(s:shellesc(a:cmd))[0], "\n")
207 call extend(buffer, call(a:callback, a:000 + [line]))
209 call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
213 call s:QuickfixSet(a:nr, extend(buffer, call(a:callback, a:000 + [0])), 'a')
215 if a:first && len(s:QuickfixGet(a:nr))
217 return a:nr < 0 ? 'cfirst' : 'lfirst'
225 function! s:UserCommandList(...) abort
226 let git = split(get(g:, 'fugitive_git_command', g:fugitive_git_executable), '\s\+')
227 let dir = a:0 ? s:Dir(a:1) : ''
229 let tree = s:Tree(dir)
231 call add(git, '--git-dir=' . FugitiveGitPath(dir))
232 elseif len(tree) && s:cpath(tree) !=# s:cpath(getcwd())
233 if fugitive#GitVersion(1, 8, 5)
234 call extend(git, ['-C', FugitiveGitPath(tree)])
236 throw 'fugitive: Git 1.8.5 or higher required to change directory'
243 function! s:UserCommand(...) abort
244 return s:shellesc(call('s:UserCommandList', a:0 ? [a:1] : []) + (a:0 ? a:2 : []))
247 let s:git_versions = {}
248 function! fugitive#GitVersion(...) abort
249 if !has_key(s:git_versions, g:fugitive_git_executable)
250 let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), '\d[^[:space:]]\+')
253 return s:git_versions[g:fugitive_git_executable]
255 let components = split(s:git_versions[g:fugitive_git_executable], '\D\+')
259 for i in range(len(a:000))
260 if a:000[i] > +get(components, i)
262 elseif a:000[i] < +get(components, i)
266 return a:000[i] ==# get(components, i)
269 let s:commondirs = {}
270 function! fugitive#CommonDir(dir) abort
274 if !has_key(s:commondirs, a:dir)
275 if getfsize(a:dir . '/HEAD') < 10
276 let s:commondirs[a:dir] = ''
277 elseif filereadable(a:dir . '/commondir')
278 let cdir = get(readfile(a:dir . '/commondir', 1), 0, '')
279 if cdir =~# '^/\|^\a:/'
280 let s:commondirs[a:dir] = s:Slash(FugitiveVimPath(cdir))
282 let s:commondirs[a:dir] = simplify(a:dir . '/' . cdir)
285 let s:commondirs[a:dir] = a:dir
288 return s:commondirs[a:dir]
291 function! s:Dir(...) abort
292 return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
295 function! s:Tree(...) abort
296 return a:0 ? FugitiveWorkTree(a:1) : FugitiveWorkTree()
299 function! s:HasOpt(args, ...) abort
300 let args = a:args[0 : index(a:args, '--')]
301 let opts = copy(a:000)
302 if type(opts[0]) == type([])
303 if empty(args) || index(opts[0], args[0]) == -1
309 if index(args, opt) != -1
315 function! s:PreparePathArgs(cmd, dir, literal) abort
316 let literal_supported = fugitive#GitVersion(1, 9)
317 if a:literal && literal_supported
318 call insert(a:cmd, '--literal-pathspecs')
320 let split = index(a:cmd, '--')
321 for i in range(split < 0 ? len(a:cmd) : split)
322 if type(a:cmd[i]) == type(0)
323 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
329 for i in range(split + 1, len(a:cmd) - 1)
330 if type(a:cmd[i]) == type(0)
331 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
333 let a:cmd[i] = fugitive#Path(a:cmd[i], './', a:dir)
334 elseif !literal_supported
335 let a:cmd[i] = substitute(a:cmd[i], '^:\%(/\|([^)]*)\)\=:\=', './', '')
341 let s:prepare_env = {
342 \ 'sequence.editor': 'GIT_SEQUENCE_EDITOR',
343 \ 'core.editor': 'GIT_EDITOR',
344 \ 'core.askpass': 'GIT_ASKPASS',
346 function! fugitive#PrepareDirEnvArgv(...) abort
347 if a:0 && type(a:1) ==# type([])
348 let cmd = a:000[1:-1] + a:1
350 let cmd = copy(a:000)
355 if cmd[i] =~# '^$\|[\/.]' && cmd[i] !~# '^-'
356 let dir = remove(cmd, 0)
357 elseif cmd[i] =~# '^--git-dir='
358 let dir = remove(cmd, 0)[10:-1]
359 elseif type(cmd[i]) ==# type(0)
360 let dir = s:Dir(remove(cmd, i))
361 elseif cmd[i] ==# '-c' && len(cmd) > i + 1
362 let key = matchstr(cmd[i+1], '^[^=]*')
363 if has_key(s:prepare_env, tolower(key)) || key !~# '\.'
364 let var = get(s:prepare_env, tolower(key), key)
365 let val = matchstr(cmd[i+1], '=\zs.*')
368 if fugitive#GitVersion(1, 8) && cmd[i+1] =~# '\.'
371 call remove(cmd, i, i + 1)
373 elseif cmd[i] =~# '^--.*pathspecs$'
374 let explicit_pathspec_option = 1
375 if fugitive#GitVersion(1, 9)
380 elseif cmd[i] !~# '^-'
389 call s:PreparePathArgs(cmd, dir, !exists('explicit_pathspec_option'))
390 return [dir, env, cmd]
393 function! s:BuildShell(dir, env, args) abort
394 let cmd = copy(a:args)
395 let tree = s:Tree(a:dir)
397 for [var, val] in items(a:env)
399 let pre .= 'set ' . var . '=' . s:shellesc(val) . '& '
401 let pre = (len(pre) ? pre : 'env ') . var . '=' . s:shellesc(val) . ' '
404 if empty(tree) || index(cmd, '--') == len(cmd) - 1
405 call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
406 elseif fugitive#GitVersion(1, 8, 5)
407 call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep')
409 let pre = 'cd ' . s:shellesc(tree) . (s:winshell() ? '& ' : '; ') . pre
411 return pre . g:fugitive_git_executable . ' ' . join(map(cmd, 's:shellesc(v:val)'))
414 function! fugitive#Prepare(...) abort
415 let [dir, env, argv] = call('fugitive#PrepareDirEnvArgv', a:000)
416 return s:BuildShell(dir, env, argv)
419 function! s:SystemError(cmd, ...) abort
421 if &shellredir ==# '>' && &shell =~# 'sh\|cmd'
422 let shellredir = &shellredir
426 set shellredir=>%s\ 2>&1
429 let out = call('system', [type(a:cmd) ==# type([]) ? fugitive#Prepare(a:cmd) : a:cmd] + a:000)
430 return [out, v:shell_error]
431 catch /^Vim\%((\a\+)\)\=:E484:/
432 let opts = ['shell', 'shellcmdflag', 'shellredir', 'shellquote', 'shellxquote', 'shellxescape', 'shellslash']
433 call filter(opts, 'exists("+".v:val) && !empty(eval("&".v:val))')
434 call map(opts, 'v:val."=".eval("&".v:val)')
435 call s:throw('failed to run `' . a:cmd . '` with ' . join(opts, ' '))
437 if exists('shellredir')
438 let &shellredir = shellredir
443 function! s:ChompError(...) abort
444 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
445 return [s:sub(out, '\n$', ''), exec_error]
448 function! s:ChompDefault(default, ...) abort
449 let [out, exec_error] = call('s:ChompError', a:000)
450 return exec_error ? a:default : out
453 function! s:LinesError(...) abort
454 let [out, exec_error] = call('s:ChompError', a:000)
455 return [len(out) && !exec_error ? split(out, "\n", 1) : [], exec_error]
458 function! s:NullError(...) abort
459 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
460 return [exec_error ? [] : split(out, "\1"), exec_error ? substitute(out, "\n$", "", "") : '', exec_error]
463 function! s:TreeChomp(...) abort
464 let cmd = call('fugitive#Prepare', a:000)
465 let [out, exec_error] = s:SystemError(cmd)
466 let out = s:sub(out, '\n$', '')
470 throw 'fugitive: error running `' . cmd . '`: ' . out
473 function! s:EchoExec(...) abort
474 echo call('s:ChompError', a:000)[0]
475 call fugitive#ReloadStatus(-1, 1)
479 function! fugitive#Head(...) abort
480 let dir = a:0 > 1 ? a:2 : s:Dir()
481 if empty(dir) || !filereadable(fugitive#Find('.git/HEAD', dir))
484 let head = readfile(fugitive#Find('.git/HEAD', dir))[0]
486 return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
487 elseif head =~# '^\x\{40,\}$'
488 let len = a:0 ? a:1 : 0
489 return len < 0 ? head : len ? head[0:len-1] : ''
495 function! fugitive#RevParse(rev, ...) abort
496 let [hash, exec_error] = s:ChompError([a:0 ? a:1 : s:Dir(), 'rev-parse', '--verify', a:rev, '--'])
497 if !exec_error && hash =~# '^\x\{40,\}$'
500 throw 'fugitive: rev-parse '.a:rev.': '.hash
503 function! s:ConfigTimestamps(dir, dict) abort
504 let files = ['/etc/gitconfig', '~/.gitconfig',
505 \ len($XDG_CONFIG_HOME) ? $XDG_CONFIG_HOME . '/git/config' : '~/.config/git/config']
507 call add(files, fugitive#Find('.git/config', a:dir))
509 call extend(files, get(a:dict, 'include.path', []))
510 return join(map(files, 'getftime(expand(v:val))'), ',')
514 function! fugitive#Config(...) abort
517 if a:0 >= 2 && type(a:2) == type({})
518 let name = substitute(a:1, '^[^.]\+\|[^.]\+$', '\L&', 'g')
519 return len(a:1) ? get(get(a:2, name, []), 0, '') : a:2
523 elseif a:0 == 1 && type(a:1) == type({})
525 elseif a:0 == 1 && a:1 =~# '^[[:alnum:]-]\+\.'
530 let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
531 let key = len(dir) ? dir : '_'
532 if has_key(s:config, key) && s:config[key][0] ==# s:ConfigTimestamps(dir, s:config[key][1])
533 let dict = s:config[key][1]
536 let [lines, message, exec_error] = s:NullError([dir, 'config', '--list', '-z'])
541 let key = matchstr(line, "^[^\n]*")
542 if !has_key(dict, key)
545 call add(dict[key], strpart(line, len(key) + 1))
547 let s:config[dir] = [s:ConfigTimestamps(dir, dict), dict]
550 return len(name) ? get(get(dict, name, []), 0, '') : dict
553 function! s:Remote(dir) abort
554 let head = FugitiveHead(0, a:dir)
555 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
557 while remote ==# '.' && i > 0
558 let head = matchstr(fugitive#Config('branch.' . head . '.merge'), 'refs/heads/\zs.*')
559 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
562 return remote =~# '^\.\=$' ? 'origin' : remote
565 function! fugitive#RemoteUrl(...) abort
566 let dir = a:0 > 1 ? a:2 : s:Dir()
567 let remote = !a:0 || a:1 =~# '^\.\=$' ? s:Remote(dir) : a:1
568 if !fugitive#GitVersion(2, 7)
569 return fugitive#Config('remote.' . remote . '.url')
571 return s:ChompDefault('', [dir, 'remote', 'get-url', remote, '--'])
574 " Section: Repository Object
576 function! s:add_methods(namespace, method_names) abort
577 for name in a:method_names
578 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
582 function! s:Command(command, line1, line2, range, bang, mods, arg, args) abort
584 if a:command =~# '^\l[[:alnum:]-]\+$'
585 return s:GitCommand(a:line1, a:line2, a:range, a:line2, a:bang, s:Mods(a:mods), '', a:command . ' ' . a:arg, [a:command] + a:args)
587 return s:{a:command}Command(a:line1, a:line2, a:range, a:line2, a:bang, s:Mods(a:mods), '', a:arg, a:args)
589 return 'echoerr ' . string(v:exception)
594 function! s:command(definition, ...) abort
595 let def = a:definition
596 if !has('patch-7.4.542')
597 let def = substitute(def, '-addr=\S\+ ', '', '')
599 if !has('patch-8.1.560')
600 let def = substitute(def, '-addr=other ', '', '')
603 call add(s:commands, def . ' execute s:Command(' . string(a:1) . ", <line1>, <count>, +'<range>', <bang>0, '<mods>', <q-args>, [<f-args>])")
605 call add(s:commands, def)
609 function! s:define_commands() abort
610 for command in s:commands
611 exe 'command! -buffer '.command
615 let s:repo_prototype = {}
618 function! fugitive#repo(...) abort
619 let dir = a:0 ? s:Dir(a:1) : (len(s:Dir()) ? s:Dir() : FugitiveExtractGitDir(expand('%:p')))
621 if has_key(s:repos, dir)
622 let repo = get(s:repos, dir)
624 let repo = {'git_dir': dir}
625 let s:repos[dir] = repo
627 return extend(repo, s:repo_prototype, 'keep')
629 call s:throw('not a Git repository')
632 function! s:repo_dir(...) dict abort
633 return join([self.git_dir]+a:000,'/')
636 function! s:repo_tree(...) dict abort
637 let dir = s:Tree(self.git_dir)
639 call s:throw('no work tree')
641 return join([dir]+a:000,'/')
645 function! s:repo_bare() dict abort
646 if self.dir() =~# '/\.git$'
649 return s:Tree(self.git_dir) ==# ''
653 function! s:repo_find(object) dict abort
654 return fugitive#Find(a:object, self.git_dir)
657 function! s:repo_translate(rev) dict abort
658 return s:Slash(fugitive#Find(substitute(a:rev, '^/', ':(top)', ''), self.git_dir))
661 function! s:repo_head(...) dict abort
662 return fugitive#Head(a:0 ? a:1 : 0, self.git_dir)
665 call s:add_methods('repo',['dir','tree','bare','find','translate','head'])
667 function! s:repo_prepare(...) dict abort
668 return call('fugitive#Prepare', [self.git_dir] + a:000)
671 function! s:repo_git_command(...) dict abort
672 let git = s:UserCommand() . ' --git-dir='.s:shellesc(self.git_dir)
673 return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
676 function! s:repo_git_chomp(...) dict abort
677 let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
678 let output = git . join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
679 return s:sub(system(output), '\n$', '')
682 function! s:repo_git_chomp_in_tree(...) dict abort
683 let cdback = s:Cd(self.tree())
685 return call(self.git_chomp, a:000, self)
691 function! s:repo_rev_parse(rev) dict abort
692 return fugitive#RevParse(a:rev, self.git_dir)
695 call s:add_methods('repo',['prepare','git_command','git_chomp','git_chomp_in_tree','rev_parse'])
697 function! s:repo_superglob(base) dict abort
698 return map(fugitive#CompleteObject(a:base, self.git_dir), 'substitute(v:val, ''\\\(.\)'', ''\1'', "g")')
701 call s:add_methods('repo',['superglob'])
703 function! s:repo_config(name) dict abort
704 return fugitive#Config(a:name, self.git_dir)
707 function! s:repo_user() dict abort
708 let username = self.config('user.name')
709 let useremail = self.config('user.email')
710 return username.' <'.useremail.'>'
713 call s:add_methods('repo',['config', 'user'])
717 function! s:DirCommitFile(path) abort
718 let vals = matchlist(s:Slash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40,\}\|[0-3]\)\(/.*\)\=$')
725 function! s:DirRev(url) abort
726 let [dir, commit, file] = s:DirCommitFile(a:url)
727 return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
730 function! s:Owner(path, ...) abort
731 let dir = a:0 ? a:1 : s:Dir()
735 let actualdir = fugitive#Find('.git/', dir)
736 let [pdir, commit, file] = s:DirCommitFile(a:path)
737 if s:cpath(dir, pdir)
738 if commit =~# '^\x\{40,\}$'
740 elseif commit ==# '2'
743 if filereadable(actualdir . 'MERGE_HEAD')
744 let merge_head = 'MERGE_HEAD'
745 elseif filereadable(actualdir . 'REBASE_HEAD')
746 let merge_head = 'REBASE_HEAD'
751 return merge_head . '^{}'
752 elseif commit ==# '1'
753 return s:TreeChomp('merge-base', 'HEAD', merge_head, '--')
756 let path = fnamemodify(a:path, ':p')
757 if s:cpath(actualdir, strpart(path, 0, len(actualdir))) && a:path =~# 'HEAD$'
758 return strpart(path, len(actualdir))
760 let refs = fugitive#Find('.git/refs', dir)
761 if s:cpath(refs . '/', path[0 : len(refs)]) && path !~# '[\/]$'
762 return strpart(path, len(refs) - 4)
767 function! fugitive#Real(url) abort
771 let [dir, commit, file] = s:DirCommitFile(a:url)
773 let tree = s:Tree(dir)
774 return FugitiveVimPath((len(tree) ? tree : dir) . file)
776 let pre = substitute(matchstr(a:url, '^\a\a\+\ze:'), '^.', '\u&', '')
777 if len(pre) && pre !=? 'fugitive' && exists('*' . pre . 'Real')
778 let url = {pre}Real(a:url)
780 let url = fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??'))
782 return FugitiveVimPath(empty(url) ? a:url : url)
785 function! fugitive#Path(url, ...) abort
789 let dir = a:0 > 1 ? a:2 : s:Dir()
790 let tree = s:Tree(dir)
792 return fugitive#Real(a:url)
794 let path = s:Slash(fugitive#Real(a:url))
797 while s:cpath(tree . '/', (cwd . '/')[0 : len(tree)])
798 if s:cpath(cwd . '/', path[0 : len(cwd)])
799 if strpart(path, len(cwd) + 1) =~# '^\.git\%(/\|$\)'
802 return a:1[0:-2] . (empty(lead) ? './' : lead) . strpart(path, len(cwd) + 1)
804 let cwd = fnamemodify(cwd, ':h')
807 return a:1[0:-2] . path
810 let temp_state = s:TempState(url)
811 if has_key(temp_state, 'bufnr')
812 let url = bufname(temp_state.bufnr)
814 let url = s:Slash(fnamemodify(url, ':p'))
815 if url =~# '/$' && s:Slash(a:url) !~# '/$'
818 let [argdir, commit, file] = s:DirCommitFile(a:url)
819 if len(argdir) && s:cpath(argdir) !=# s:cpath(dir)
821 elseif len(dir) && s:cpath(url[0 : len(dir)]) ==# s:cpath(dir . '/')
822 let file = '/.git'.url[strlen(dir) : -1]
823 elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/')
824 let file = url[len(tree) : -1]
825 elseif s:cpath(url) ==# s:cpath(tree)
828 if empty(file) && a:1 =~# '^$\|^[.:]/$'
829 return FugitiveGitPath(fugitive#Real(a:url))
831 return substitute(file, '^/', a:1, '')
834 function! s:Relative(...) abort
835 return fugitive#Path(@%, a:0 ? a:1 : ':(top)', a:0 > 1 ? a:2 : s:Dir())
838 function! fugitive#Find(object, ...) abort
839 if type(a:object) == type(0)
840 let name = bufname(a:object)
841 return FugitiveVimPath(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name)
842 elseif a:object =~# '^[~$]'
843 let prefix = matchstr(a:object, '^[~$]\i*')
844 let owner = expand(prefix)
845 return FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix)))
846 elseif s:Slash(a:object) =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
847 return FugitiveVimPath(a:object)
848 elseif s:Slash(a:object) =~# '^\.\.\=\%(/\|$\)'
849 return FugitiveVimPath(simplify(getcwd() . '/' . a:object))
851 let dir = a:0 ? a:1 : s:Dir()
853 let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs.*', '', '')
854 let dir = FugitiveExtractGitDir(file)
856 return fnamemodify(FugitiveVimPath(len(file) ? file : a:object), ':p')
859 let rev = s:Slash(a:object)
860 let tree = s:Tree(dir)
861 let base = len(tree) ? tree : 'fugitive://' . dir . '//0'
863 let f = len(tree) ? tree . '/.git' : dir
864 elseif rev =~# '^\.git/'
865 let f = substitute(rev, '^\.git', '', '')
866 let cdir = fugitive#CommonDir(dir)
867 if f =~# '^/\.\./\.\.\%(/\|$\)'
868 let f = simplify(len(tree) ? tree . f[3:-1] : dir . f)
869 elseif f =~# '^/\.\.\%(/\|$\)'
870 let f = base . f[3:-1]
871 elseif cdir !=# dir && (
872 \ f =~# '^/\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' ||
873 \ f !~# '^/\%(index$\|index\.lock$\|\w*MSG$\|\w*HEAD$\|logs/\w*HEAD$\|logs$\|rebase-\w\+\)\%(/\|$\)' &&
874 \ getftime(FugitiveVimPath(dir . f)) < 0 && getftime(FugitiveVimPath(cdir . f)) >= 0)
875 let f = simplify(cdir . f)
877 let f = simplify(dir . f)
881 elseif rev =~# '^\.\%(/\|$\)'
882 let f = base . rev[1:-1]
883 elseif rev =~# '^::\%(/\|\a\+\:\)'
885 elseif rev =~# '^::\.\.\=\%(/\|$\)'
886 let f = simplify(getcwd() . '/' . rev[2:-1])
888 let f = base . '/' . rev[2:-1]
889 elseif rev =~# '^:\%([0-3]:\)\=\.\.\=\%(/\|$\)\|^:[0-3]:\%(/\|\a\+:\)'
890 let f = rev =~# '^:\%([0-3]:\)\=\.' ? simplify(getcwd() . '/' . matchstr(rev, '\..*')) : rev[3:-1]
891 if s:cpath(base . '/', (f . '/')[0 : len(base)])
892 let f = 'fugitive://' . dir . '//' . +matchstr(rev, '^:\zs\d\ze:') . '/' . strpart(f, len(base) + 1)
894 let altdir = FugitiveExtractGitDir(f)
895 if len(altdir) && !s:cpath(dir, altdir)
896 return fugitive#Find(a:object, altdir)
899 elseif rev =~# '^:[0-3]:'
900 let f = 'fugitive://' . dir . '//' . rev[1] . '/' . rev[3:-1]
902 if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && s:cpath(fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(dir)]) ==# s:cpath(dir . '/') && filereadable($GIT_INDEX_FILE)
903 let f = fnamemodify($GIT_INDEX_FILE, ':p')
905 let f = fugitive#Find('.git/index', dir)
907 elseif rev =~# '^:(\%(top\|top,literal\|literal,top\|literal\))'
908 let f = matchstr(rev, ')\zs.*')
909 if f=~# '^\.\.\=\%(/\|$\)'
910 let f = simplify(getcwd() . '/' . f)
911 elseif f !~# '^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
912 let f = base . '/' . f
914 elseif rev =~# '^:/\@!'
915 let f = 'fugitive://' . dir . '//0/' . rev[1:-1]
918 let commit = substitute(matchstr(rev, '^[^:]\+\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '')
919 let file = substitute(matchstr(rev, '^[^:]\+\zs:.*'), '^:', '/', '')
920 if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:'
921 let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1]
922 if s:cpath(base . '/', (file . '/')[0 : len(base)])
923 let file = '/' . strpart(file, len(base) + 1)
925 let altdir = FugitiveExtractGitDir(file)
926 if len(altdir) && !s:cpath(dir, altdir)
927 return fugitive#Find(a:object, altdir)
932 if commit !~# '^[0-9a-f]\{40,\}$'
933 let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit, '--']), '\<[0-9a-f]\{40,\}\>')
936 let f = 'fugitive://' . dir . '//' . commit . file
938 let f = base . '/' . substitute(rev, '^:/:\=\|^[^:]\+:', '', '')
942 return FugitiveVimPath(f)
945 function! s:Generate(rev, ...) abort
946 return fugitive#Find(a:rev, a:0 ? a:1 : s:Dir())
949 function! s:DotRelative(path, ...) abort
950 let cwd = a:0 ? a:1 : getcwd()
951 let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
952 if len(cwd) && s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
953 return '.' . strpart(path, len(cwd))
958 function! fugitive#Object(...) abort
959 let dir = a:0 > 1 ? a:2 : s:Dir()
960 let [fdir, rev] = s:DirRev(a:0 ? a:1 : @%)
961 if s:cpath(dir) !=# s:cpath(fdir)
964 let tree = s:Tree(dir)
965 if empty(rev) && empty(tree)
967 let rev = fugitive#Path(a:0 ? a:1 : @%, './', dir)
968 if rev =~# '^\./.git\%(/\|$\)'
969 return fnamemodify(a:0 ? a:1 : @%, ':p' . (rev =~# '/$' ? '' : ':s?/$??'))
972 if rev !~# '^\.\%(/\|$\)' || s:cpath(getcwd(), tree)
975 return tree . rev[1:-1]
979 let s:var = '\%(%\|#<\=\d\+\|##\=\)'
980 let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
981 let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
983 function! s:BufName(var) abort
985 return bufname(get(s:TempState(), 'bufnr', ''))
986 elseif a:var =~# '^#\d*$'
987 let nr = get(s:TempState(bufname(+a:var[1:-1])), 'bufnr', '')
988 return bufname(nr ? nr : +a:var[1:-1])
994 function! s:ExpandVarLegacy(str) abort
995 if get(g:, 'fugitive_legacy_quoting', 1)
996 return substitute(a:str, '\\\ze[%#!]', '', 'g')
1002 function! s:ExpandVar(other, var, flags, esc, ...) abort
1003 let cwd = a:0 ? a:1 : getcwd()
1005 return a:other[1:-1]
1006 elseif a:other =~# '^'''
1007 return s:ExpandVarLegacy(substitute(a:other[1:-2], "''", "'", "g"))
1008 elseif a:other =~# '^"'
1009 return s:ExpandVarLegacy(substitute(a:other[1:-2], '""', '"', "g"))
1010 elseif a:other =~# '^!'
1011 let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
1012 let owner = s:Owner(buffer)
1013 return len(owner) ? owner : '@'
1016 let file = s:DotRelative(fugitive#Real(s:BufName(a:var)), cwd)
1018 let flag = matchstr(flags, s:flag)
1019 let flags = strpart(flags, len(flag))
1021 let file = s:DotRelative(file, cwd)
1023 let file = fnamemodify(file, flag)
1026 let file = s:Slash(file)
1027 return (len(a:esc) ? shellescape(file) : file)
1030 function! s:Expand(rev, ...) abort
1031 if a:rev =~# '^:[0-3]$'
1032 let file = a:rev . ':%'
1033 elseif a:rev =~# '^>[~^]\|^>@{\|^>$'
1034 let file = 'HEAD' . a:rev[1:-1] . ':%'
1035 elseif a:rev =~# '^>[> ]\@!'
1036 let file = 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_getvar(var) dict abort
1381 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().getvar() which has been removed. Replace it with the local variable or getbufvar()"
1384 function! s:buffer_getline(lnum) dict abort
1385 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().getline() which has been removed. Replace it with getline() or getbufline()"
1388 function! s:buffer_repo() dict abort
1389 return fugitive#repo(self['#'])
1392 function! s:buffer_type(...) dict abort
1393 return getbufvar(self['#'], 'fugitive_type')
1396 function! s:buffer_spec() dict abort
1397 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().spec() which has been removed. Replace it with bufname(), expand('%:p'), etc"
1400 function! s:buffer_name() dict abort
1401 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().name() which has been removed. Replace it with bufname(), expand('%:p'), etc"
1404 function! s:buffer_commit() dict abort
1405 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().commit() which has been removed. Replace it with matchstr(FugitiveParse()[0], '^\x\+')"
1408 function! s:buffer_relative(...) dict abort
1409 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().relative() which has been removed. Replace it with FugitivePath(@%, " . string(a:0 ? a:1 : '') . ")"
1412 function! s:buffer_path(...) dict abort
1413 throw "fugitive: A third-party plugin or vimrc is calling fugitive#buffer().path() which has been removed. Replace it with FugitivePath(@%, " . string(a:0 ? a:1 : '') . ")"
1416 call s:add_methods('buffer',['getvar','getline','repo','type','spec','name','commit','path','relative'])
1418 " Section: Completion
1420 function! s:FilterEscape(items, ...) abort
1421 let items = copy(a:items)
1422 if a:0 && type(a:1) == type('')
1423 call filter(items, 'strpart(v:val, 0, strlen(a:1)) ==# a:1')
1425 return map(items, 's:fnameescape(v:val)')
1428 function! s:GlobComplete(lead, pattern) abort
1431 elseif v:version >= 704
1432 let results = glob(a:lead . a:pattern, 0, 1)
1434 let results = split(glob(a:lead . a:pattern), "\n")
1436 call map(results, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val')
1437 call map(results, 'v:val[ strlen(a:lead) : -1 ]')
1441 function! fugitive#CompletePath(base, ...) abort
1442 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1443 let tree = s:Tree(dir) . '/'
1444 let strip = '^\%(:/:\=\|:(top)\|:(top,literal)\|:(literal,top)\|:(literal)\)'
1445 let base = substitute(a:base, strip, '', '')
1446 if base =~# '^\.git/'
1447 let pattern = s:gsub(base[5:-1], '/', '*&').'*'
1448 let matches = s:GlobComplete(dir . '/', pattern)
1449 let cdir = fugitive#CommonDir(dir)
1450 if len(cdir) && s:cpath(dir) !=# s:cpath(cdir)
1451 call extend(matches, s:GlobComplete(cdir . '/', pattern))
1453 call s:Uniq(matches)
1454 call map(matches, "'.git/' . v:val")
1455 elseif base =~# '^\~/'
1456 let matches = map(s:GlobComplete(expand('~/'), base[2:-1] . '*'), '"~/" . v:val')
1457 elseif a:base =~# '^/\|^\a\+:\|^\.\.\=/\|^:(literal)'
1458 let matches = s:GlobComplete('', base . '*')
1459 elseif len(tree) > 1
1460 let matches = s:GlobComplete(tree, s:gsub(base, '/', '*&').'*')
1464 call map(matches, 's:fnameescape(s:Slash(matchstr(a:base, strip) . v:val))')
1468 function! fugitive#PathComplete(...) abort
1469 return call('fugitive#CompletePath', a:000)
1472 function! fugitive#CompleteObject(base, ...) abort
1473 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1475 let tree = s:Tree(dir) . '/'
1477 if len(tree) > 1 && s:cpath(tree, cwd[0 : len(tree) - 1])
1478 let subdir = strpart(cwd, len(tree)) . '/'
1481 if a:base =~# '^\.\=/\|^:(' || a:base !~# ':'
1483 if a:base =~# '^refs/'
1484 let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)')
1485 elseif a:base !~# '^\.\=/\|^:('
1486 let heads = ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'refs/']
1487 let heads += sort(s:LinesError(["rev-parse","--symbolic","--branches","--tags","--remotes"], dir)[0])
1488 if filereadable(fugitive#Find('.git/refs/stash', dir))
1489 let heads += ["stash"]
1490 let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0])
1492 call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
1493 let results += heads
1495 call map(results, 's:fnameescape(v:val)')
1497 let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base)
1501 elseif a:base =~# '^:'
1502 let entries = s:LinesError(['ls-files','--stage'], dir)[0]
1503 if a:base =~# ':\./'
1504 call map(entries, 'substitute(v:val, "\\M\t\\zs" . subdir, "./", "")')
1506 call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
1507 if a:base !~# '^:[0-3]\%(:\|$\)'
1508 call filter(entries,'v:val[1] == "0"')
1509 call map(entries,'v:val[2:-1]')
1513 let tree = matchstr(a:base, '.*[:/]')
1514 let entries = s:LinesError(['ls-tree', substitute(tree, ':\zs\./', '\=subdir', '')], dir)[0]
1515 call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
1516 call map(entries,'tree.s:sub(v:val,".*\t","")')
1519 return s:FilterEscape(entries, a:base)
1522 function! s:CompleteSub(subcommand, A, L, P, ...) abort
1523 let pre = strpart(a:L, 0, a:P)
1525 return fugitive#CompletePath(a:A)
1526 elseif a:A =~# '^-' || a:A is# 0
1527 return s:FilterEscape(split(s:ChompDefault('', a:subcommand, '--git-completion-helper'), ' '), a:A)
1529 return fugitive#CompleteObject(a:A, s:Dir())
1530 elseif type(a:1) == type(function('tr'))
1531 return call(a:1, [a:A, a:L, a:P])
1533 return s:FilterEscape(a:1, a:A)
1537 function! s:CompleteRevision(A, L, P) abort
1538 return s:FilterEscape(['HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'ORIG_HEAD'] +
1539 \ s:LinesError('rev-parse', '--symbolic', '--branches', '--tags', '--remotes')[0], a:A)
1542 function! s:CompleteRemote(A, L, P) abort
1543 let remote = matchstr(a:L, '\u\w*[! ] *\zs\S\+\ze ')
1545 let matches = s:LinesError('ls-remote', remote)[0]
1546 call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
1547 call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
1549 let matches = s:LinesError('remote')[0]
1551 return s:FilterEscape(matches, a:A)
1554 " Section: Buffer auto-commands
1556 function! s:ReplaceCmd(cmd) abort
1557 let temp = tempname()
1558 let [err, exec_error] = s:TempCmd(temp, a:cmd)
1560 call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
1562 let temp = s:Resolve(temp)
1563 let fn = expand('%:p')
1564 silent exe 'keepalt file '.temp
1565 let modelines = &modelines
1568 silent keepjumps noautocmd edit!
1570 let &modelines = modelines
1572 silent exe 'keepalt file '.s:fnameescape(fn)
1573 catch /^Vim\%((\a\+)\)\=:E302:/
1576 if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
1577 silent execute 'bwipeout '.bufnr('$')
1582 function! s:QueryLog(refspec) abort
1583 let lines = s:LinesError(['log', '-n', '256', '--format=%h%x09%s', a:refspec, '--'])[0]
1584 call map(lines, 'split(v:val, "\t")')
1585 call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
1589 function! s:FormatLog(dict) abort
1590 return a:dict.commit . ' ' . a:dict.subject
1593 function! s:FormatRebase(dict) abort
1594 return a:dict.status . ' ' . a:dict.commit . ' ' . a:dict.subject
1597 function! s:FormatFile(dict) abort
1598 return a:dict.status . ' ' . a:dict.filename
1601 function! s:Format(val) abort
1602 if type(a:val) == type({})
1603 return s:Format{a:val.type}(a:val)
1604 elseif type(a:val) == type([])
1605 return map(copy(a:val), 's:Format(v:val)')
1611 function! s:AddHeader(key, value) abort
1616 while !empty(getline(before))
1619 call append(before - 1, [a:key . ':' . (len(a:value) ? ' ' . a:value : '')])
1620 if before == 1 && line('$') == 2
1625 function! s:AddSection(label, lines, ...) abort
1626 let note = a:0 ? a:1 : ''
1627 if empty(a:lines) && empty(note)
1630 call append(line('$'), ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines))
1633 function! fugitive#BufReadStatus() abort
1634 let amatch = s:Slash(expand('%:p'))
1635 let b:fugitive_type = 'index'
1636 unlet! b:fugitive_reltime
1638 silent doautocmd BufReadPre
1639 let cmd = [fnamemodify(amatch, ':h')]
1640 setlocal noro ma nomodeline buftype=nowrite
1641 if s:cpath(fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : fugitive#Find('.git/index'), ':p')) !=# s:cpath(amatch)
1642 let cmd += ['-c', 'GIT_INDEX_FILE=' . amatch]
1644 let cmd += ['status', '--porcelain', '-bz']
1645 let [output, message, exec_error] = s:NullError(cmd)
1647 throw 'fugitive: ' . message
1650 let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
1652 if head =~# '\.\.\.'
1653 let [head, pull] = split(head, '\.\.\.')
1655 elseif head ==# 'HEAD' || empty(head)
1656 let head = FugitiveHead(11)
1662 let b:fugitive_status = {'Staged': {}, 'Unstaged': {}}
1663 let [staged, unstaged, untracked] = [[], [], []]
1665 while i < len(output)
1666 let line = output[i]
1667 let file = line[3:-1]
1673 if line[0:1] =~# '[RC]'
1674 let files = output[i] . ' -> ' . file
1677 if line[0] !~# '[ ?!#]'
1678 call add(staged, {'type': 'File', 'status': line[0], 'filename': files})
1680 if line[0:1] ==# '??'
1681 call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
1682 elseif line[1] !~# '[ !#]'
1683 call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files})
1688 let b:fugitive_status['Staged'][dict.filename] = dict.status
1690 for dict in unstaged
1691 let b:fugitive_status['Unstaged'][dict.filename] = dict.status
1694 let config = fugitive#Config()
1696 let pull_type = 'Pull'
1698 let rebase = fugitive#Config('branch.' . branch . '.rebase', config)
1700 let rebase = fugitive#Config('pull.rebase', config)
1702 if rebase =~# '^\%(true\|yes\|on\|1\|interactive\)$'
1703 let pull_type = 'Rebase'
1704 elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
1705 let pull_type = 'Merge'
1709 let push_remote = fugitive#Config('branch.' . branch . '.pushRemote', config)
1710 if empty(push_remote)
1711 let push_remote = fugitive#Config('remote.pushDefault', config)
1713 let push = len(push_remote) && len(branch) ? push_remote . '/' . branch : ''
1719 let unpulled = s:QueryLog(head . '..' . pull)
1724 let unpushed = s:QueryLog(push . '..' . head)
1729 if isdirectory(fugitive#Find('.git/rebase-merge/'))
1730 let rebasing_dir = fugitive#Find('.git/rebase-merge/')
1731 elseif isdirectory(fugitive#Find('.git/rebase-apply/'))
1732 let rebasing_dir = fugitive#Find('.git/rebase-apply/')
1736 let rebasing_head = 'detached HEAD'
1737 if exists('rebasing_dir') && filereadable(rebasing_dir . 'git-rebase-todo')
1738 let rebasing_head = substitute(readfile(rebasing_dir . 'head-name')[0], '\C^refs/heads/', '', '')
1740 let lines = readfile(rebasing_dir . 'git-rebase-todo')
1742 let hash = matchstr(line, '^[^a-z].*\s\zs[0-9a-f]\{4,\}\ze\.\.')
1748 if getfsize(rebasing_dir . 'done') > 0
1749 let done = readfile(rebasing_dir . 'done')
1750 call map(done, 'substitute(v:val, ''^\l\+\>'', "done", "")')
1751 let done[-1] = substitute(done[-1], '^\l\+\>', 'stop', '')
1752 let lines = done + lines
1756 let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
1757 if len(match) && match[1] !~# 'exec\|merge\|label'
1758 call add(rebasing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': strpart(match[2], 0, len), 'subject': match[3]})
1763 let diff = {'Staged': [], 'Unstaged': []}
1765 let diff['Staged'] =
1766 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached'])[0]
1769 let diff['Unstaged'] =
1770 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix'])[0]
1772 let b:fugitive_diff = diff
1773 let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}})
1774 let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}}
1776 silent keepjumps %delete_
1778 call s:AddHeader('Head', head)
1779 call s:AddHeader(pull_type, pull)
1781 call s:AddHeader('Push', push)
1783 call s:AddSection('Rebasing ' . rebasing_head, rebasing)
1784 call s:AddSection('Untracked', untracked)
1785 call s:AddSection('Unstaged', unstaged)
1786 let unstaged_end = len(unstaged) ? line('$') : 0
1787 call s:AddSection('Staged', staged)
1788 let staged_end = len(staged) ? line('$') : 0
1789 call s:AddSection('Unpushed to ' . push, unpushed)
1790 call s:AddSection('Unpulled from ' . pull, unpulled)
1792 setlocal nomodified readonly noswapfile
1793 silent doautocmd BufReadPost
1794 setlocal nomodifiable
1795 if &bufhidden ==# ''
1796 setlocal bufhidden=delete
1798 let b:dispatch = ':Gfetch --all'
1799 call fugitive#MapJumps()
1800 call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1801 call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
1802 call s:Map('n', 's', ":<C-U>execute <SID>Do('Stage',0)<CR>", '<silent>')
1803 call s:Map('x', 's', ":<C-U>execute <SID>Do('Stage',1)<CR>", '<silent>')
1804 call s:Map('n', 'u', ":<C-U>execute <SID>Do('Unstage',0)<CR>", '<silent>')
1805 call s:Map('x', 'u', ":<C-U>execute <SID>Do('Unstage',1)<CR>", '<silent>')
1806 call s:Map('n', 'U', ":exe <SID>EchoExec('reset', '-q')<CR>", '<silent>')
1807 call s:MapMotion('gu', "exe <SID>StageJump(v:count, 'Untracked', 'Unstaged')")
1808 call s:MapMotion('gU', "exe <SID>StageJump(v:count, 'Unstaged', 'Untracked')")
1809 call s:MapMotion('gs', "exe <SID>StageJump(v:count, 'Staged')")
1810 call s:MapMotion('gp', "exe <SID>StageJump(v:count, 'Unpushed')")
1811 call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
1812 call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
1813 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>')
1814 call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1815 call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
1816 call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
1817 call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
1818 call s:Map('n', ">", ":<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>", '<silent>')
1819 call s:Map('x', "=", ":<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1820 call s:Map('x', "<", ":<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1821 call s:Map('x', ">", ":<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1822 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>')
1823 call s:Map('n', 'dd', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>", '<silent>')
1824 call s:Map('n', 'dh', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1825 call s:Map('n', 'ds', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1826 call s:Map('n', 'dp', ":<C-U>execute <SID>StageDiffEdit()<CR>", '<silent>')
1827 call s:Map('n', 'dv', ":<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>", '<silent>')
1828 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
1829 call s:Map('n', 'P', ":<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>", '<silent>')
1830 call s:Map('x', 'P', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1831 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>')
1832 call s:Map('x', 'p', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1833 call s:Map('n', 'I', ":<C-U>execute <SID>StagePatch(line('.'),line('.'))<CR>", '<silent>')
1834 call s:Map('x', 'I', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1835 if empty(mapcheck('q', 'n'))
1836 nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>
1838 call s:Map('n', 'gq', ":<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>", '<silent>')
1839 call s:Map('n', 'R', ":echohl WarningMsg<Bar>echo 'Reloading is automatic. Use :e to force'<Bar>echohl NONE<CR>", '<silent>')
1840 call s:Map('n', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1841 call s:Map('x', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1842 call s:Map('n', 'X', ":<C-U>execute <SID>StageDelete(line('.'), 0, v:count)<CR>", '<silent>')
1843 call s:Map('x', 'X', ":<C-U>execute <SID>StageDelete(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1844 call s:Map('n', 'gI', ":<C-U>execute <SID>StageIgnore(line('.'), line('.'), v:count)<CR>", '<silent>')
1845 call s:Map('x', 'gI', ":<C-U>execute <SID>StageIgnore(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1846 call s:Map('n', '.', ':<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>')
1847 call s:Map('x', '.', ':<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>')
1848 setlocal filetype=fugitive
1850 for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']]
1851 while len(getline(lnum))
1852 let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*')
1853 if has_key(expanded[section], filename)
1854 call s:StageInline('show', lnum)
1860 let b:fugitive_reltime = reltime()
1863 return 'echoerr ' . string(v:exception)
1867 function! fugitive#FileReadCmd(...) abort
1868 let amatch = a:0 ? a:1 : expand('<amatch>')
1869 let [dir, rev] = s:DirRev(amatch)
1870 let line = a:0 > 1 ? a:2 : line("'[")
1872 return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
1874 if rev !~# ':' && s:ChompDefault('', [dir, 'cat-file', '-t', rev]) =~# '^\%(commit\|tag\)$'
1875 let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev, '--')
1877 let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev)
1879 return line . 'read !' . escape(cmd, '!#%')
1882 function! fugitive#FileWriteCmd(...) abort
1883 let tmp = tempname()
1884 let amatch = a:0 ? a:1 : expand('<amatch>')
1885 let autype = a:0 > 1 ? 'Buf' : 'File'
1886 if exists('#' . autype . 'WritePre')
1887 execute 'doautocmd ' . autype . 'WritePre ' . s:fnameescape(amatch)
1890 let [dir, commit, file] = s:DirCommitFile(amatch)
1891 if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$'))
1892 return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch)
1894 silent execute "'[,']write !".fugitive#Prepare(dir, 'hash-object', '-w', '--stdin', '--').' > '.tmp
1895 let sha1 = readfile(tmp)[0]
1896 let old_mode = matchstr(s:SystemError([dir, 'ls-files', '--stage', '.' . file])[0], '^\d\+')
1898 let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
1900 let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1]
1901 let [error, exec_error] = s:SystemError([dir, 'update-index', '--index-info'], info . "\n")
1904 if exists('#' . autype . 'WritePost')
1905 execute 'doautocmd ' . autype . 'WritePost ' . s:fnameescape(amatch)
1909 return 'echoerr '.string('fugitive: '.error)
1916 let s:nomodeline = (v:version >= 704 ? '<nomodeline>' : '')
1918 function! fugitive#BufReadCmd(...) abort
1919 let amatch = a:0 ? a:1 : expand('<amatch>')
1921 let [dir, rev] = s:DirRev(amatch)
1923 return 'echo "Invalid Fugitive URL"'
1926 let b:fugitive_type = 'stage'
1928 let [b:fugitive_type, exec_error] = s:ChompError([dir, 'cat-file', '-t', rev])
1929 if exec_error && rev =~# '^:0'
1930 let sha = s:ChompDefault('', dir, 'write-tree', '--prefix=' . rev[3:-1])
1931 let exec_error = empty(sha)
1932 let b:fugitive_type = exec_error ? '' : 'tree'
1935 let error = b:fugitive_type
1936 unlet b:fugitive_type
1938 if empty(&bufhidden)
1939 setlocal bufhidden=delete
1942 let &l:readonly = !filewritable(fugitive#Find('.git/index', dir))
1943 return 'silent doautocmd BufNewFile'
1945 setlocal readonly nomodifiable
1946 return 'silent doautocmd BufNewFile|echo ' . string(error)
1948 elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
1949 return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'")
1951 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
1952 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
1956 if b:fugitive_type !=# 'blob'
1960 setlocal noreadonly modifiable
1961 let pos = getpos('.')
1962 silent keepjumps %delete_
1966 silent doautocmd BufReadPre
1967 if b:fugitive_type ==# 'tree'
1968 let b:fugitive_display_format = b:fugitive_display_format % 2
1969 if b:fugitive_display_format
1970 call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev])
1973 let sha = s:TreeChomp(dir, 'rev-parse', '--verify', rev, '--')
1975 call s:ReplaceCmd([dir, 'show', '--no-color', sha])
1977 elseif b:fugitive_type ==# 'tag'
1978 let b:fugitive_display_format = b:fugitive_display_format % 2
1979 if b:fugitive_display_format
1980 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1982 call s:ReplaceCmd([dir, 'cat-file', '-p', rev])
1984 elseif b:fugitive_type ==# 'commit'
1985 let b:fugitive_display_format = b:fugitive_display_format % 2
1986 if b:fugitive_display_format
1987 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1989 call s:ReplaceCmd([dir, 'show', '--no-color', '--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b', rev])
1990 keepjumps call search('^parent ')
1991 if getline('.') ==# 'parent '
1992 silent keepjumps delete_
1994 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
1996 keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
1998 silent keepjumps delete_
2000 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
2003 elseif b:fugitive_type ==# 'stage'
2004 call s:ReplaceCmd([dir, 'ls-files', '--stage'])
2005 elseif b:fugitive_type ==# 'blob'
2006 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
2009 keepjumps call setpos('.',pos)
2010 setlocal nomodified noswapfile
2011 let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
2012 let &l:readonly = !modifiable || !filewritable(fugitive#Find('.git/index', dir))
2013 if empty(&bufhidden)
2014 setlocal bufhidden=delete
2016 let &l:modifiable = modifiable
2017 if b:fugitive_type !=# 'blob'
2018 setlocal filetype=git foldmethod=syntax
2019 call s:Map('n', 'a', ":<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2020 call s:Map('n', 'i', ":<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2022 call fugitive#MapJumps()
2026 return 'silent doautocmd' . s:nomodeline .
2027 \ ' BufReadPost' . (modifiable ? '' : '|setl nomodifiable')
2029 return 'echoerr ' . string(v:exception)
2033 function! fugitive#BufWriteCmd(...) abort
2034 return fugitive#FileWriteCmd(a:0 ? a:1 : expand('<amatch>'), 1)
2037 function! fugitive#SourceCmd(...) abort
2038 let amatch = a:0 ? a:1 : expand('<amatch>')
2039 let temp = s:BlobTemp(amatch)
2041 return 'noautocmd source ' . s:fnameescape(amatch)
2043 if !exists('g:virtual_scriptnames')
2044 let g:virtual_scriptnames = {}
2046 let g:virtual_scriptnames[temp] = amatch
2047 return 'source ' . s:fnameescape(temp)
2050 " Section: Temp files
2052 if !exists('s:temp_files')
2053 let s:temp_files = {}
2056 function! s:TempState(...) abort
2057 return get(s:temp_files, s:cpath(fnamemodify(a:0 ? a:1 : @%, ':p')), {})
2060 function! s:TempReadPre(file) abort
2061 if has_key(s:temp_files, s:cpath(a:file))
2062 let dict = s:temp_files[s:cpath(a:file)]
2064 setlocal bufhidden=delete nobuflisted
2065 setlocal buftype=nowrite
2066 if has_key(dict, 'modifiable')
2067 let &l:modifiable = dict.modifiable
2070 let b:git_dir = dict.dir
2071 call extend(b:, {'fugitive_type': 'temp'}, 'keep')
2076 function! s:TempReadPost(file) abort
2077 if has_key(s:temp_files, s:cpath(a:file))
2078 let dict = s:temp_files[s:cpath(a:file)]
2079 if has_key(dict, 'filetype') && dict.filetype !=# &l:filetype
2080 let &l:filetype = dict.filetype
2082 setlocal foldmarker=<<<<<<<,>>>>>>>
2083 if empty(mapcheck('q', 'n'))
2084 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>
2087 call s:Map('n', 'gq', ":<C-U>bdelete<CR>", '<silent> <unique>')
2093 augroup fugitive_temp
2095 autocmd BufReadPre * exe s:TempReadPre( expand('<amatch>:p'))
2096 autocmd BufReadPost * exe s:TempReadPost(expand('<amatch>:p'))
2101 function! s:GitCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
2103 let [args, after] = s:SplitExpandChain(a:arg, s:Tree(dir))
2105 let cmd = s:StatusCommand(a:line1, a:line2, a:range, a:count, a:bang, a:mods, a:reg, '', [])
2106 return (empty(cmd) ? 'exe' : cmd) . after
2108 let alias = get(s:Aliases(dir), args[0], '!')
2109 if get(args, 1, '') !=# '--help' && alias !~# '^!\|[\"'']' && !filereadable(s:ExecPath() . '/git-' . args[0])
2110 \ && !(has('win32') && filereadable(s:ExecPath() . '/git-' . args[0] . '.exe'))
2111 call remove(args, 0)
2112 call extend(args, split(alias, '\s\+'), 'keep')
2114 let name = substitute(args[0], '\%(^\|-\)\(\l\)', '\u\1', 'g')
2115 if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
2118 return 'exe ' . string(s:{name}Subcommand(a:line1, a:count, a:range, a:bang, a:mods, args[1:-1])) . after
2120 return 'echoerr ' . string(v:exception)
2123 if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' ||
2124 \ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
2125 \ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
2126 return s:OpenExec((a:count > 0 ? a:count : '') . (a:count ? 'split' : 'edit'), a:mods, args, dir) . after
2128 if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') ||
2129 \ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive') ||
2130 \ index(['--paginate', '-p'], args[0]) >= 0
2131 let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
2133 if &autowrite || &autowriteall | silent! wall | endif
2134 return mods . (a:count ? 'split' : 'edit') . ' term://' . s:fnameescape(s:UserCommand(dir, args)) . '|startinsert' . after
2135 elseif has('terminal')
2136 if &autowrite || &autowriteall | silent! wall | endif
2137 return 'exe ' . string(mods . 'terminal ' . (a:count ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . after
2140 if has('gui_running') && !has('win32')
2141 call insert(args, '--no-pager')
2144 if has('nvim') && executable('env')
2145 let pre .= 'env GIT_TERMINAL_PROMPT=0 '
2147 return 'exe ' . string('!' . escape(pre . s:UserCommand(dir, args), '!#%')) . after
2150 let s:exec_paths = {}
2151 function! s:ExecPath() abort
2152 if !has_key(s:exec_paths, g:fugitive_git_executable)
2153 let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
2155 return s:exec_paths[g:fugitive_git_executable]
2158 function! s:Subcommands() abort
2159 let exec_path = s:ExecPath()
2160 return map(split(glob(exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(exec_path)+5 : -1],"\\.exe$","")')
2164 function! s:Aliases(dir) abort
2165 if !has_key(s:aliases, a:dir)
2166 let s:aliases[a:dir] = {}
2167 let lines = s:NullError([a:dir, 'config', '-z', '--get-regexp', '^alias[.]'])[0]
2169 let s:aliases[a:dir][matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*')
2172 return s:aliases[a:dir]
2175 function! fugitive#CompleteGit(lead, ...) abort
2176 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
2177 let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
2178 let subcmd = matchstr(pre, '\u\w*[! ] *\zs[[:alnum:]-]\+\ze ')
2180 let results = sort(s:Subcommands() + keys(s:Aliases(dir)))
2181 elseif pre =~# ' -- '
2182 return fugitive#CompletePath(a:lead, dir)
2183 elseif a:lead =~# '^-'
2184 let results = split(s:ChompDefault('', dir, subcmd, '--git-completion-helper'), ' ')
2186 return fugitive#CompleteObject(a:lead, dir)
2188 return filter(results, 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
2191 function! fugitive#Complete(...) abort
2192 return call('fugitive#CompleteGit', a:000)
2195 call s:command("-bang -nargs=? -range=-1 -addr=other -complete=customlist,fugitive#CompleteGit Git", "Git")
2196 call s:command("-bang -nargs=? -range=-1 -addr=other -complete=customlist,fugitive#CompleteGit G", "Git")
2198 " Section: :Gcd, :Glcd
2200 function! s:DirComplete(A, L, P) abort
2201 return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
2204 function! s:DirArg(path) abort
2205 let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
2206 if path =~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
2209 return FugitiveVimPath((empty(s:Tree()) ? s:Dir() : s:Tree()) . '/' . path)
2213 call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Gcd :exe s:DirCheck()|exe 'cd<bang>' s:fnameescape(s:DirArg(<q-args>))")
2214 call s:command("-bar -bang -nargs=? -complete=customlist,s:DirComplete Glcd :exe s:DirCheck()|exe 'lcd<bang>' s:fnameescape(s:DirArg(<q-args>))")
2218 call s:command("-bar -bang -range=-1 -addr=other Gstatus", "Status")
2220 function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
2221 let dir = a:0 ? a:1 : s:Dir()
2224 let mods = s:Mods(a:mods, &splitbelow ? 'botright' : 'topleft')
2225 let file = fugitive#Find(':', dir)
2226 let arg = ' +setl\ foldmethod=syntax\ foldlevel=1\|let\ w:fugitive_status=FugitiveGitDir() ' .
2227 \ s:fnameescape(file)
2228 for winnr in range(1, winnr('$'))
2229 if s:cpath(file, fnamemodify(bufname(winbufnr(winnr)), ':p'))
2231 call s:ReloadStatus()
2233 call s:ExpireStatus(dir)
2234 exe winnr . 'wincmd w'
2236 let w:fugitive_status = dir
2242 return mods . 'edit' . (a:bang ? '!' : '') . arg
2244 return mods . 'pedit' . arg . '|wincmd P'
2246 return mods . (a:count > 0 ? a:count : '') . 'split' . arg
2249 return 'echoerr ' . string(v:exception)
2254 function! s:StageJump(offset, section, ...) abort
2255 let line = search('^\%(' . a:section . '\)', 'nw')
2257 let line = search('^\%(' . a:1 . '\)', 'nw')
2262 for i in range(a:offset)
2263 call search(s:file_commit_pattern . '\|^$', 'W')
2264 if empty(getline('.')) && a:0 && getline(line('.') + 1) =~# '^\%(' . a:1 . '\)'
2265 call search(s:file_commit_pattern . '\|^$', 'W')
2267 if empty(getline('.'))
2271 call s:StageReveal()
2273 call s:StageReveal()
2280 function! s:StageSeek(info, fallback) abort
2282 if empty(info.section)
2285 let line = search('^' . info.section, 'wn')
2287 for section in get({'Staged': ['Unstaged', 'Untracked'], 'Unstaged': ['Untracked', 'Staged'], 'Untracked': ['Unstaged', 'Staged']}, info.section, [])
2288 let line = search('^' . section, 'wn')
2290 return line + (info.index > 0 ? 1 : 0)
2296 while len(getline(line))
2297 let filename = matchstr(getline(line), '^[A-Z?] \zs.*')
2299 \ ((info.filename[-1:-1] ==# '/' && filename[0 : len(info.filename) - 1] ==# info.filename) ||
2300 \ (filename[-1:-1] ==# '/' && filename ==# info.filename[0 : len(filename) - 1]) ||
2301 \ filename ==# info.filename)
2305 if getline(line+1) !~# '^@'
2306 exe s:StageInline('show', line)
2308 if getline(line+1) !~# '^@'
2311 let type = info.sigil ==# '-' ? '-' : '+'
2313 while offset < info.offset
2315 if getline(line) =~# '^@'
2316 let offset = +matchstr(getline(line), type . '\zs\d\+') - 1
2317 elseif getline(line) =~# '^[ ' . type . ']'
2319 elseif getline(line) !~# '^[ @\+-]'
2326 let commit = matchstr(getline(line), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\+')
2327 if len(commit) && commit ==# info.commit
2333 let i += getline(line) !~# '^[ @\+-]'
2336 return exists('backup') ? backup : line - 1
2339 function! s:ReloadStatus(...) abort
2340 call s:ExpireStatus(-1)
2341 if get(b:, 'fugitive_type', '') !=# 'index'
2344 let original_lnum = a:0 ? a:1 : line('.')
2345 let info = s:StageInfo(original_lnum)
2346 call fugitive#BufReadStatus()
2347 exe s:StageSeek(info, original_lnum)
2352 let s:last_time = reltime()
2353 if !exists('s:last_times')
2354 let s:last_times = {}
2357 function! s:ExpireStatus(bufnr) abort
2359 let s:last_time = reltime()
2362 let dir = s:Dir(a:bufnr)
2364 let s:last_times[s:cpath(dir)] = reltime()
2369 function! FugitiveReloadCheck() abort
2370 let t = b:fugitive_reltime
2371 return [t, reltimestr(reltime(s:last_time, t)),
2372 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t))]
2375 function! s:ReloadWinStatus(...) abort
2376 if get(b:, 'fugitive_type', '') !=# 'index' || &modified
2379 if !exists('b:fugitive_reltime')
2380 exe s:ReloadStatus()
2383 let t = b:fugitive_reltime
2384 if reltimestr(reltime(s:last_time, t)) =~# '-\|\d\{10\}\.' ||
2385 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t)) =~# '-\|\d\{10\}\.'
2386 exe s:ReloadStatus()
2390 function! s:ReloadTabStatus(...) abort
2391 let mytab = tabpagenr()
2392 let tab = a:0 ? a:1 : mytab
2393 for winnr in range(1, tabpagewinnr(tab, '$'))
2394 if getbufvar(tabpagebuflist(tab)[winnr-1], 'fugitive_type') ==# 'index'
2395 execute 'tabnext '.tab
2397 execute winnr.'wincmd w'
2398 let restorewinnr = 1
2401 call s:ReloadWinStatus()
2403 if exists('restorewinnr')
2407 execute 'tabnext '.mytab
2411 unlet! t:fugitive_reload_status
2414 function! fugitive#ReloadStatus(...) abort
2415 call s:ExpireStatus(a:0 ? a:1 : -2)
2416 if a:0 > 1 ? a:2 : s:CanAutoReloadStatus()
2418 let t:fugitive_reload_status = t
2419 for tabnr in exists('*settabvar') ? range(1, tabpagenr('$')) : []
2420 call settabvar(tabnr, 'fugitive_reload_status', t)
2422 call s:ReloadTabStatus()
2424 call s:ReloadWinStatus()
2428 function! s:CanAutoReloadStatus() abort
2429 return get(g:, 'fugitive_autoreload_status', !has('win32'))
2432 augroup fugitive_status
2434 autocmd BufWritePost * call fugitive#ReloadStatus(-1, 0)
2435 autocmd ShellCmdPost * nested call fugitive#ReloadStatus()
2436 autocmd BufDelete term://* nested call fugitive#ReloadStatus()
2438 autocmd FocusGained * call fugitive#ReloadStatus(-2, 0)
2440 autocmd BufEnter index,index.lock
2441 \ call s:ReloadWinStatus()
2443 \ if exists('t:fugitive_reload_status') |
2444 \ call s:ReloadTabStatus() |
2448 function! s:StageInfo(...) abort
2449 let lnum = a:0 ? a:1 : line('.')
2450 let sigil = matchstr(getline(lnum), '^[ @\+-]')
2453 let type = sigil ==# '-' ? '-' : '+'
2454 while lnum > 0 && getline(lnum) !~# '^@'
2455 if getline(lnum) =~# '^[ '.type.']'
2460 let offset += matchstr(getline(lnum), type.'\zs\d\+')
2461 while getline(lnum) =~# '^[ @\+-]'
2465 let slnum = lnum + 1
2468 while len(getline(slnum - 1)) && empty(section)
2470 let section = matchstr(getline(slnum), '^\u\l\+\ze.* (\d\+)$')
2471 if empty(section) && getline(slnum) !~# '^[ @\+-]'
2475 let text = matchstr(getline(lnum), '^[A-Z?] \zs.*')
2476 return {'section': section,
2477 \ 'heading': getline(slnum),
2481 \ 'relative': reverse(split(text, ' -> ')),
2482 \ 'paths': map(reverse(split(text, ' -> ')), 's:Tree() . "/" . v:val'),
2483 \ 'commit': matchstr(getline(lnum), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze '),
2484 \ 'status': matchstr(getline(lnum), '^[A-Z?]\ze \|^\%(\x\x\x\)\@!\l\+\ze [0-9a-f]'),
2488 function! s:Selection(arg1, ...) abort
2490 let arg1 = line('.')
2492 elseif a:arg1 ==# 'v'
2493 let arg1 = line("'<")
2494 let arg2 = line("'>")
2497 let arg2 = a:0 ? a:1 : 0
2501 let last = first - arg2 + 1
2507 while getline(first) =~# '^$\|^[A-Z][a-z]'
2510 if first > last || &filetype !=# 'fugitive'
2514 while getline(flnum) =~# '^[ @\+-]'
2517 let slnum = flnum + 1
2520 while len(getline(slnum - 1)) && empty(section)
2522 let heading = matchstr(getline(slnum), '^\u\l\+.* (\d\+)$')
2523 if empty(heading) && getline(slnum) !~# '^[ @\+-]'
2529 \ 'heading': heading,
2530 \ 'section': matchstr(heading, '^\u\l\+\ze.* (\d\+)$'),
2538 let line = getline(flnum)
2539 let lnum = first - (arg1 == flnum ? 0 : 1)
2540 let root = s:Tree() . '/'
2542 if line =~# '^\u\l\+\ze.* (\d\+)$'
2543 let template.heading = getline(lnum)
2544 let template.section = matchstr(template.heading, '^\u\l\+\ze.* (\d\+)$')
2545 let template.index = 0
2546 elseif line =~# '^[ @\+-]'
2547 let template.index -= 1
2548 if !results[-1].patch
2549 let results[-1].patch = lnum
2551 let results[-1].lnum = lnum
2552 elseif line =~# '^[A-Z?] '
2553 let filename = matchstr(line, '^[A-Z?] \zs.*')
2554 call add(results, extend(deepcopy(template), {
2556 \ 'filename': filename,
2557 \ 'relative': reverse(split(filename, ' -> ')),
2558 \ 'paths': map(reverse(split(filename, ' -> ')), 'root . v:val'),
2559 \ 'status': matchstr(line, '^[A-Z?]'),
2561 elseif line =~# '^\x\x\x\+ '
2562 call add(results, extend({
2564 \ 'commit': matchstr(line, '^\x\x\x\+'),
2565 \ }, template, 'keep'))
2566 elseif line =~# '^\l\+ \x\x\x\+ '
2567 call add(results, extend({
2569 \ 'commit': matchstr(line, '^\l\+ \zs\x\x\x\+'),
2570 \ 'status': matchstr(line, '^\l\+'),
2571 \ }, template, 'keep'))
2574 let template.index += 1
2575 let line = getline(lnum)
2577 if len(results) && results[0].patch && arg2 == 0
2578 while getline(results[0].patch) =~# '^[ \+-]'
2579 let results[0].patch -= 1
2581 while getline(results[0].lnum + 1) =~# '^[ \+-]'
2582 let results[0].lnum += 1
2588 function! s:StageArgs(visual) abort
2591 for record in s:Selection(a:visual ? 'v' : 'n')
2592 if len(record.commit)
2593 call add(commits, record.commit)
2595 call extend(paths, record.paths)
2597 if s:cpath(s:Tree(), getcwd())
2598 call map(paths, 'fugitive#Path(v:val, "./")')
2600 return join(map(commits + paths, 's:fnameescape(v:val)'), ' ')
2603 function! s:Do(action, visual) abort
2604 let line = getline('.')
2606 if !a:0 && !v:count && line =~# '^[A-Z][a-z]'
2607 let header = matchstr(line, '^\S\+\ze:')
2608 if len(header) && exists('*s:Do' . a:action . header . 'Header')
2609 let reload = s:Do{a:action}{header}Header(matchstr(line, ': \zs.*')) > 0
2611 let section = matchstr(line, '^\S\+')
2612 if exists('*s:Do' . a:action . section . 'Heading')
2613 let reload = s:Do{a:action}{section}Heading(line) > 0
2616 return reload ? s:ReloadStatus() : ''
2618 let selection = s:Selection(a:visual ? 'v' : 'n')
2622 call filter(selection, 'v:val.section ==# selection[0].section')
2626 for record in selection
2627 if exists('*s:Do' . a:action . record.section)
2628 let status = s:Do{a:action}{record.section}(record)
2635 let reload = reload || (status > 0)
2638 execute record.lnum + 1
2642 return 'echoerr ' . string(v:exception)
2645 execute s:ReloadStatus()
2647 if exists('success')
2648 call s:StageReveal()
2654 function! s:StageReveal(...) abort
2655 let begin = a:0 ? a:1 : line('.')
2656 if getline(begin) =~# '^@'
2658 while getline(end) =~# '^[ \+-]'
2661 elseif getline(begin) =~# '^commit '
2663 while end < line('$') && getline(end + 1) !~# '^commit '
2666 elseif getline(begin) =~# s:section_pattern
2668 while len(getline(end + 1))
2673 while line('.') > line('w0') + &scrolloff && end > line('w$')
2674 execute "normal! \<C-E>"
2679 let s:file_pattern = '^[A-Z?] .\|^diff --'
2680 let s:file_commit_pattern = s:file_pattern . '\|^\%(\l\{3,\} \)\=[0-9a-f]\{4,\} '
2681 let s:item_pattern = s:file_commit_pattern . '\|^@@'
2683 function! s:NextHunk(count) abort
2684 if &filetype ==# 'fugitive' && getline('.') =~# s:file_pattern
2685 exe s:StageInline('show')
2687 for i in range(a:count)
2688 if &filetype ==# 'fugitive'
2689 call search(s:file_pattern . '\|^@', 'W')
2690 if getline('.') =~# s:file_pattern
2691 exe s:StageInline('show')
2692 if getline(line('.') + 1) =~# '^@'
2697 call search('^@@', 'W')
2700 call s:StageReveal()
2704 function! s:PreviousHunk(count) abort
2705 for i in range(a:count)
2706 if &filetype ==# 'fugitive'
2707 let lnum = search(s:file_pattern . '\|^@','Wbn')
2708 call s:StageInline('show', lnum)
2709 call search('^? .\|^@','Wb')
2711 call search('^@@', 'Wb')
2714 call s:StageReveal()
2718 function! s:NextFile(count) abort
2719 for i in range(a:count)
2720 exe s:StageInline('hide')
2721 if !search(s:file_pattern, 'W')
2725 exe s:StageInline('hide')
2729 function! s:PreviousFile(count) abort
2730 exe s:StageInline('hide')
2731 for i in range(a:count)
2732 if !search(s:file_pattern, 'Wb')
2735 exe s:StageInline('hide')
2740 function! s:NextItem(count) abort
2741 for i in range(a:count)
2742 if !search(s:item_pattern, 'W') && getline('.') !~# s:item_pattern
2743 call search('^commit ', 'W')
2746 call s:StageReveal()
2750 function! s:PreviousItem(count) abort
2751 for i in range(a:count)
2752 if !search(s:item_pattern, 'Wbe') && getline('.') !~# s:item_pattern
2753 call search('^commit ', 'Wbe')
2756 call s:StageReveal()
2760 let s:section_pattern = '^[A-Z][a-z][^:]*$'
2761 let s:section_commit_pattern = s:section_pattern . '\|^commit '
2763 function! s:NextSection(count) abort
2764 let orig = line('.')
2765 if getline('.') !~# '^commit '
2768 for i in range(a:count)
2769 if !search(s:section_commit_pattern, 'W')
2773 if getline('.') =~# s:section_commit_pattern
2774 call s:StageReveal()
2775 return getline('.') =~# s:section_pattern ? '+' : ':'
2781 function! s:PreviousSection(count) abort
2782 let orig = line('.')
2783 if getline('.') !~# '^commit '
2786 for i in range(a:count)
2787 if !search(s:section_commit_pattern . '\|\%^', 'bW')
2791 if getline('.') =~# s:section_commit_pattern || line('.') == 1
2792 call s:StageReveal()
2793 return getline('.') =~# s:section_pattern ? '+' : ':'
2799 function! s:NextSectionEnd(count) abort
2801 if empty(getline('.'))
2804 for i in range(a:count)
2805 if !search(s:section_commit_pattern, 'W')
2809 return search('^.', 'Wb')
2812 function! s:PreviousSectionEnd(count) abort
2814 for i in range(a:count)
2815 if search(s:section_commit_pattern, 'Wb') <= 1
2825 return search('^.', 'Wb')
2828 function! s:PatchSearchExpr(reverse) abort
2829 let line = getline('.')
2830 if col('.') ==# 1 && line =~# '^[+-]'
2831 if line =~# '^[+-]\{3\} '
2832 let pattern = '^[+-]\{3\} ' . substitute(escape(strpart(line, 4), '^$.*[]~\'), '^\w/', '\\w/', '') . '$'
2834 let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
2837 return '?' . escape(pattern, '/') . "\<CR>"
2839 return '/' . escape(pattern, '/?') . "\<CR>"
2842 return a:reverse ? '#' : '*'
2845 function! s:StageInline(mode, ...) abort
2846 if &filetype !=# 'fugitive'
2849 let lnum1 = a:0 ? a:1 : line('.')
2850 let lnum = lnum1 + 1
2851 if a:0 > 1 && a:2 == 0
2852 let info = s:StageInfo(lnum - 1)
2853 if empty(info.paths) && len(info.section)
2854 while len(getline(lnum))
2863 while lnum > 0 && getline(lnum) =~# '^[ @\+-]'
2866 let info = s:StageInfo(lnum)
2867 if !has_key(b:fugitive_diff, info.section)
2870 if getline(lnum + 1) =~# '^[ @\+-]'
2871 let lnum2 = lnum + 1
2872 while getline(lnum2 + 1) =~# '^[ @\+-]'
2875 if a:mode !=# 'show'
2876 setlocal modifiable noreadonly
2877 exe 'silent keepjumps ' . (lnum + 1) . ',' . lnum2 . 'delete _'
2878 call remove(b:fugitive_expanded[info.section], info.filename)
2879 setlocal nomodifiable readonly nomodified
2883 if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADMRU]$' || a:mode ==# 'hide'
2890 for line in b:fugitive_diff[info.section]
2891 if mode ==# 'await' && line[0] ==# '@'
2892 let mode = 'capture'
2894 if mode !=# 'head' && line !~# '^[ @\+-]'
2900 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '--- ' . info.relative[-1]
2902 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '+++ ' . info.relative[0]
2904 elseif mode ==# 'capture'
2905 call add(diff, line)
2906 elseif line[0] ==# '@'
2912 setlocal modifiable noreadonly
2913 silent call append(lnum, diff)
2914 let b:fugitive_expanded[info.section][info.filename] = [start, len(diff)]
2915 setlocal nomodifiable readonly nomodified
2921 function! s:NextExpandedHunk(count) abort
2922 for i in range(a:count)
2923 call s:StageInline('show', line('.'), 1)
2924 call search(s:file_pattern . '\|^@','W')
2929 function! s:StageDiff(diff) abort
2930 let lnum = line('.')
2931 let info = s:StageInfo(lnum)
2932 let prefix = info.offset > 0 ? '+' . info.offset : ''
2933 if empty(info.paths) && info.section ==# 'Staged'
2934 return 'Git! diff --no-ext-diff --cached'
2935 elseif empty(info.paths)
2936 return 'Git! diff --no-ext-diff'
2937 elseif len(info.paths) > 1
2938 execute 'Gedit' . prefix s:fnameescape(':0:' . info.paths[0])
2939 return a:diff . '! HEAD:'.s:fnameescape(info.paths[1])
2940 elseif info.section ==# 'Staged' && info.sigil ==# '-'
2941 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2942 return a:diff . '! :0:%'
2943 elseif info.section ==# 'Staged'
2944 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2945 return a:diff . '! @:%'
2946 elseif info.sigil ==# '-'
2947 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2948 return a:diff . '! :(top)%'
2950 execute 'Gedit' prefix s:fnameescape(':(top)'.info.paths[0])
2955 function! s:StageDiffEdit() abort
2956 let info = s:StageInfo(line('.'))
2957 let arg = (empty(info.paths) ? s:Tree() : info.paths[0])
2958 if info.section ==# 'Staged'
2959 return 'Git! diff --no-ext-diff --cached '.s:fnameescape(arg)
2960 elseif info.status ==# '?'
2961 call s:TreeChomp('add', '--intent-to-add', '--', arg)
2962 return s:ReloadStatus()
2964 return 'Git! diff --no-ext-diff '.s:fnameescape(arg)
2968 function! s:StageApply(info, reverse, extra) abort
2969 if a:info.status ==# 'R'
2970 call s:throw('fugitive: patching renamed file not yet supported')
2972 let cmd = ['apply', '-p0', '--recount'] + a:extra
2974 let start = info.patch
2976 let lines = getline(start, end)
2977 if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
2980 while getline(end) =~# '^[-+ ]'
2982 if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2983 call add(lines, ' ' . getline(end)[1:-1])
2986 while start > 0 && getline(start) !~# '^@'
2988 if getline(start) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2989 call insert(lines, ' ' . getline(start)[1:-1])
2990 elseif getline(start) =~# '^@'
2991 call insert(lines, getline(start))
2995 throw 'fugitive: cold not find hunk'
2996 elseif getline(start) !~# '^@@ '
2997 throw 'fugitive: cannot apply conflict hunk'
2999 let i = b:fugitive_expanded[info.section][info.filename][0]
3001 while get(b:fugitive_diff[info.section], i, '@') !~# '^@'
3002 call add(head, b:fugitive_diff[info.section][i])
3005 call extend(lines, head, 'keep')
3006 let temp = tempname()
3007 call writefile(lines, temp)
3009 call add(cmd, '--reverse')
3011 call extend(cmd, ['--', temp])
3012 let [output, exec_error] = s:ChompError(cmd)
3016 call s:throw(output)
3019 function! s:StageDelete(lnum1, lnum2, count) abort
3023 for info in s:Selection(a:lnum1, a:lnum2)
3024 if empty(info.paths)
3027 let hash = s:TreeChomp('hash-object', '-w', '--', info.paths[0])
3032 call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
3033 elseif info.status ==# '?'
3034 call s:TreeChomp('clean', '-f', '--', info.paths[0])
3036 call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
3038 call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
3039 elseif info.status =~# '[ADU]' &&
3040 \ get(b:fugitive_status[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, '') =~# '[AU]'
3041 call s:TreeChomp('checkout', info.section ==# 'Staged' ? '--ours' : '--theirs', '--', info.paths[0])
3042 elseif info.status ==# 'U'
3043 call s:TreeChomp('rm', '--', info.paths[0])
3044 elseif info.status ==# 'A'
3045 call s:TreeChomp('rm', '-f', '--', info.paths[0])
3046 elseif info.section ==# 'Unstaged'
3047 call s:TreeChomp('checkout', '--', info.paths[0])
3049 call s:TreeChomp('checkout', 'HEAD^{}', '--', info.paths[0])
3051 call add(restore, ':Gsplit ' . s:fnameescape(info.relative[0]) . '|Gread ' . hash[0:6])
3054 let err = '|echoerr ' . string(v:exception)
3059 exe s:ReloadStatus()
3060 call s:StageReveal()
3061 return 'checktime|redraw|echomsg ' . string('To restore, ' . join(restore, '|')) . err
3064 function! s:StageIgnore(lnum1, lnum2, count) abort
3066 for info in s:Selection(a:lnum1, a:lnum2)
3067 call extend(paths, info.relative)
3069 call map(paths, '"/" . v:val')
3070 exe 'Gsplit' (a:count ? '.gitignore' : '.git/info/exclude')
3071 let last = line('$')
3072 if last == 1 && empty(getline(1))
3073 call setline(last, paths)
3075 call append(last, paths)
3081 function! s:DoToggleHeadHeader(value) abort
3082 exe 'edit' s:fnameescape(s:Dir())
3083 call search('\C^index$', 'wc')
3086 function! s:DoStageUnpushedHeading(heading) abort
3087 let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
3091 let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
3092 call feedkeys(':Gpush ' . remote . ' ' . 'HEAD:' . branch)
3095 function! s:DoToggleUnpushedHeading(heading) abort
3096 return s:DoStageUnpushedHeading(a:heading)
3099 function! s:DoStageUnpushed(record) abort
3100 let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
3104 let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
3105 call feedkeys(':Gpush ' . remote . ' ' . a:record.commit . ':' . branch)
3108 function! s:DoToggleUnpushed(record) abort
3109 return s:DoStageUnpushed(a:record)
3112 function! s:DoUnstageUnpulledHeading(heading) abort
3113 call feedkeys(':Grebase')
3116 function! s:DoToggleUnpulledHeading(heading) abort
3117 call s:DoUnstageUnpulledHeading(a:heading)
3120 function! s:DoUnstageUnpulled(record) abort
3121 call feedkeys(':Grebase ' . a:record.commit)
3124 function! s:DoToggleUnpulled(record) abort
3125 call s:DoUnstageUnpulled(a:record)
3128 function! s:DoUnstageUnpushed(record) abort
3129 call feedkeys(':Grebase --autosquash ' . a:record.commit . '^')
3132 function! s:DoToggleStagedHeading(...) abort
3133 call s:TreeChomp('reset', '-q')
3137 function! s:DoUnstageStagedHeading(heading) abort
3138 return s:DoToggleStagedHeading(a:heading)
3141 function! s:DoToggleUnstagedHeading(...) abort
3142 call s:TreeChomp('add', '-u')
3146 function! s:DoStageUnstagedHeading(heading) abort
3147 return s:DoToggleUnstagedHeading(a:heading)
3150 function! s:DoToggleUntrackedHeading(...) abort
3151 call s:TreeChomp('add', '.')
3155 function! s:DoStageUntrackedHeading(heading) abort
3156 return s:DoToggleUntrackedHeading(a:heading)
3159 function! s:DoToggleStaged(record) abort
3161 return s:StageApply(a:record, 1, ['--cached'])
3163 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3168 function! s:DoUnstageStaged(record) abort
3169 return s:DoToggleStaged(a:record)
3172 function! s:DoToggleUnstaged(record) abort
3173 if a:record.patch && a:record.status !=# 'A'
3174 return s:StageApply(a:record, 0, ['--cached'])
3176 call s:TreeChomp(['add', '-A', '--'] + a:record.paths)
3181 function! s:DoStageUnstaged(record) abort
3182 return s:DoToggleUnstaged(a:record)
3185 function! s:DoUnstageUnstaged(record) abort
3186 if a:record.status ==# 'A'
3187 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3194 function! s:DoToggleUntracked(record) abort
3195 call s:TreeChomp(['add', '--'] + a:record.paths)
3199 function! s:DoStageUntracked(record) abort
3200 return s:DoToggleUntracked(a:record)
3203 function! s:StagePatch(lnum1,lnum2) abort
3208 for lnum in range(a:lnum1,a:lnum2)
3209 let info = s:StageInfo(lnum)
3210 if empty(info.paths) && info.section ==# 'Staged'
3211 return 'Git reset --patch'
3212 elseif empty(info.paths) && info.section ==# 'Unstaged'
3213 return 'Git add --patch'
3214 elseif empty(info.paths) && info.section ==# 'Untracked'
3215 return 'Git add --interactive'
3216 elseif empty(info.paths)
3220 if info.section ==# 'Staged'
3221 let reset += info.relative
3222 elseif info.section ==# 'Untracked'
3223 let intend += info.paths
3224 elseif info.status !~# '^D'
3225 let add += info.relative
3230 call s:TreeChomp(['add', '--intent-to-add', '--'] + intend)
3233 execute "Git add --patch -- ".join(map(add,'s:fnameescape(v:val)'))
3236 execute "Git reset --patch -- ".join(map(reset,'s:fnameescape(v:val)'))
3239 return 'echoerr ' . string(v:exception)
3241 return s:ReloadStatus()
3244 " Section: :Gcommit, :Grevert
3246 function! s:CommitInteractive(line1, line2, range, bang, mods, args, patch) abort
3247 let status = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
3248 let status = len(status) ? status . '|' : ''
3250 return status . 'if search("^Unstaged")|exe "normal >"|exe "+"|endif'
3252 return status . 'if search("^Untracked\\|^Unstaged")|exe "+"|endif'
3256 function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
3257 let mods = substitute(s:Mods(a:mods), '\C\<tab\>', '-tab', 'g')
3258 let dir = a:0 ? a:1 : s:Dir()
3259 let tree = s:Tree(dir)
3260 let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
3261 let outfile = tempname()
3264 let command = 'set GIT_EDITOR=false& '
3266 let command = 'env GIT_EDITOR=false '
3270 while get(argv, i, '--') !=# '--'
3271 if argv[i] =~# '^-[apzsneiovq].'
3272 call insert(argv, argv[i][0:1])
3273 let argv[i+1] = '-' . argv[i+1][2:-1]
3278 let command .= s:UserCommand(dir, ['commit'] + argv)
3279 if (&autowrite || &autowriteall) && !a:0
3282 if s:HasOpt(argv, '-i', '--interactive')
3283 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 0)
3284 elseif s:HasOpt(argv, '-p', '--patch')
3285 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 1)
3287 let [error_string, exec_error] = s:TempCmd(outfile, command)
3288 let errors = split(error_string, "\n")
3290 if !has('gui_running')
3294 echo join(errors, "\n")
3295 if filereadable(outfile)
3296 echo join(readfile(outfile), "\n")
3298 call fugitive#ReloadStatus(dir, 1)
3301 let error = get(errors,-2,get(errors,-1,'!'))
3302 if error =~# 'false''\=\.$'
3304 while get(argv, i, '--') !=# '--'
3305 if argv[i] =~# '^\%(-[eips]\|-[CcFm].\+\|--edit\|--interactive\|--patch\|--signoff\|--reedit-message=.*\|--reuse-message=.*\|--file=.*\|--message=.*\)$'
3306 call remove(argv, i)
3307 elseif argv[i] =~# '^\%(-[CcFm]\|--reedit-message\|--reuse-message\|--file\|--message\)$'
3308 call remove(argv, i, i + 1)
3310 if argv[i] =~# '^--cleanup\>'
3316 call insert(argv, '--no-signoff', i)
3317 call insert(argv, '--no-interactive', i)
3318 call insert(argv, '--no-edit', i)
3319 if !exists('cleanup')
3320 call insert(argv, '--cleanup=strip')
3322 call extend(argv, ['-F', msgfile], 'keep')
3323 if (bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&modified) || a:line2 == 0
3324 execute mods . 'keepalt edit' s:fnameescape(msgfile)
3325 elseif s:HasOpt(argv, '-v') || mods =~# '\<tab\>'
3326 execute mods . 'keepalt -tabedit' s:fnameescape(msgfile)
3328 execute mods . 'keepalt split' s:fnameescape(msgfile)
3330 let b:fugitive_commit_arguments = argv
3331 setlocal bufhidden=wipe filetype=gitcommit
3333 elseif empty(errors)
3334 let out = readfile(outfile)
3335 echo get(out, -1, '') =~# 'stash\|\d' ? get(out, -2, '') : get(out, -1, '')
3338 echo join(errors, "\n")
3343 return 'echoerr ' . string(v:exception)
3345 call delete(outfile)
3349 function! s:RevertSubcommand(line1, line2, range, bang, mods, args) abort
3351 let no_commit = s:HasOpt(a:args, '-n', '--no-commit', '--no-edit', '--abort', '--continue', '--quit')
3352 let cmd = s:UserCommand(dir, ['revert'] + (no_commit ? [] : ['-n']) + a:args)
3353 let [out, exec_error] = s:SystemError(cmd)
3354 call fugitive#ReloadStatus(-1, 1)
3355 if no_commit || exec_error
3356 return 'echo ' . string(substitute(out, "\n$", '', ''))
3358 return s:CommitSubcommand(a:line1, a:line2, a:range, a:bang, a:mods, [], dir)
3361 function! s:CommitComplete(A, L, P) abort
3362 if a:A =~# '^--fixup=\|^--squash='
3363 let commits = s:LinesError(['log', '--pretty=format:%s', '@{upstream}..'])[0]
3364 let pre = matchstr(a:A, '^--\w*=''\=') . ':/^'
3366 call map(commits, 'pre . string(tr(v:val, "|\"^$*[]", "......."))[1:-1]')
3367 call filter(commits, 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
3370 return s:FilterEscape(map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")'), a:A)
3373 return s:CompleteSub('commit', a:A, a:L, a:P, function('fugitive#CompletePath'))
3378 function! s:RevertComplete(A, L, P) abort
3379 return s:CompleteSub('revert', a:A, a:L, a:P, function('s:CompleteRevision'))
3382 function! s:FinishCommit() abort
3383 let buf = +expand('<abuf>')
3384 let args = getbufvar(buf, 'fugitive_commit_arguments')
3386 call setbufvar(buf, 'fugitive_commit_arguments', [])
3387 if getbufvar(buf, 'fugitive_commit_rebase')
3388 call setbufvar(buf, 'fugitive_commit_rebase', 0)
3389 let s:rebase_continue = s:Dir(buf)
3391 return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf))
3396 call s:command("-nargs=? -range=-1 -complete=customlist,s:CommitComplete Gcommit", "commit")
3397 call s:command("-nargs=? -range=-1 -complete=customlist,s:RevertComplete Grevert", "revert")
3399 " Section: :Gmerge, :Grebase, :Gpull
3401 function! s:MergeComplete(A, L, P) abort
3402 return s:CompleteSub('merge', a:A, a:L, a:P, function('s:CompleteRevision'))
3405 function! s:RebaseComplete(A, L, P) abort
3406 return s:CompleteSub('rebase', a:A, a:L, a:P, function('s:CompleteRevision'))
3409 function! s:PullComplete(A, L, P) abort
3410 return s:CompleteSub('pull', a:A, a:L, a:P, function('s:CompleteRemote'))
3413 function! s:RebaseSequenceAborter() abort
3414 if !exists('s:rebase_sequence_aborter')
3415 let temp = tempname() . '.sh'
3418 \ 'echo exec false | cat - "$1" > "$1.fugitive"',
3419 \ 'mv "$1.fugitive" "$1"'],
3421 let s:rebase_sequence_aborter = temp
3423 return s:rebase_sequence_aborter
3426 function! fugitive#Cwindow() abort
3427 if &buftype == 'quickfix'
3431 if &buftype == 'quickfix'
3437 let s:common_efm = ''
3439 \ . '%+Eusage:%.%#,'
3440 \ . '%+Eerror:%.%#,'
3441 \ . '%+Efatal:%.%#,'
3442 \ . '%-G%.%#%\e[K%.%#,'
3443 \ . '%-G%.%#%\r%.%\+'
3445 let s:rebase_abbrevs = {
3459 function! s:RebaseEdit(cmd, dir) abort
3460 let rebase_todo = s:fnameescape(fugitive#Find('.git/rebase-merge/git-rebase-todo', a:dir))
3462 if filereadable(rebase_todo)
3463 let new = readfile(rebase_todo)
3467 for i in range(len(new))
3468 if new[i] =~# '^\l\+\s\+[0-9a-f]\{5,\}\>'
3469 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3471 let sha_length = len(s:TreeChomp(a:dir, 'rev-parse', '--short', sha))
3473 let shortened_sha = strpart(sha, 0, sha_length)
3474 let shas[shortened_sha] = sha
3475 let new[i] = substitute(new[i], sha, shortened_sha, '')
3478 call writefile(new, rebase_todo)
3480 return a:cmd . ' +setlocal\ bufhidden=wipe\|' . escape('let b:fugitive_rebase_shas = ' . string(shas), ' ') . ' ' . rebase_todo
3483 function! s:MergeRebase(cmd, bang, mods, args, ...) abort
3484 let dir = a:0 ? a:1 : s:Dir()
3486 let mods = s:Mods(a:mods)
3487 if a:cmd =~# '^rebase' && s:HasOpt(args, '-i', '--interactive')
3488 let cmd = fugitive#Prepare(dir, '-c', 'sequence.editor=sh ' . s:RebaseSequenceAborter(), 'rebase') . ' ' . s:shellesc(args)
3489 let out = system(cmd)[0:-2]
3490 for file in ['end', 'msgnum']
3491 let file = fugitive#Find('.git/rebase-merge/' . file, dir)
3492 if !filereadable(file)
3493 return 'echoerr ' . string("fugitive: " . out)
3495 call writefile([readfile(file)[0] - 1], file)
3497 call writefile([], fugitive#Find('.git/rebase-merge/done', dir))
3501 return s:RebaseEdit(mods . 'split', dir)
3502 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--edit-todo') && filereadable(fugitive#Find('.git/rebase-merge/git-rebase-todo', dir))
3503 return s:RebaseEdit(mods . 'split', dir)
3504 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--continue') && !a:0
3505 let rdir = fugitive#Find('.git/rebase-merge', dir)
3506 let exec_error = s:ChompError([dir, 'diff-index', '--cached', '--quiet', 'HEAD', '--'])[1]
3507 if exec_error && isdirectory(rdir)
3508 if getfsize(rdir . '/amend') <= 0
3509 return 'exe ' . string(mods . 'Gcommit -n -F ' . s:fnameescape(rdir .'/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3510 elseif readfile(rdir . '/amend')[0] ==# fugitive#Head(-1, dir)
3511 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(rdir . '/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3515 let had_merge_msg = filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3518 let argv += s:AskPassArgs(dir) + ['pull', '--progress']
3520 call add(argv, a:cmd)
3522 if !s:HasOpt(args, '--no-edit', '--abort', '-m') && a:cmd !=# 'rebase'
3523 call add(argv, '--edit')
3525 if a:cmd ==# 'rebase' && s:HasOpt(args, '--autosquash') && !s:HasOpt(args, '--interactive', '-i')
3526 call add(argv, '--interactive')
3528 call extend(argv, args)
3530 let [mp, efm] = [&l:mp, &l:efm]
3532 let cdback = s:Cd(s:Tree(dir))
3533 let &l:errorformat = ''
3534 \ . '%-Gerror:%.%#false''.,'
3535 \ . '%-G%.%# ''git commit'' %.%#,'
3536 \ . '%+Emerge:%.%#,'
3537 \ . s:common_efm . ','
3538 \ . '%+ECannot %.%#: You have unstaged changes.,'
3539 \ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
3540 \ . '%+EThere is no tracking information for the current branch.,'
3541 \ . '%+EYou are not currently on a branch. Please specify which,'
3542 \ . '%+I %#git rebase --continue,'
3543 \ . 'CONFLICT (%m): %f deleted in %.%#,'
3544 \ . 'CONFLICT (%m): Merge conflict in %f,'
3545 \ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
3546 \ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
3547 \ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
3548 \ . '%+ECONFLICT %.%#,'
3549 \ . '%+EKONFLIKT %.%#,'
3550 \ . '%+ECONFLIT %.%#,'
3551 \ . "%+EXUNG \u0110\u1ed8T %.%#,"
3552 \ . "%+E\u51b2\u7a81 %.%#,"
3554 if a:cmd =~# '^merge' && empty(args) &&
3555 \ (had_merge_msg || isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
3556 \ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
3557 let cmd = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
3559 let cmd = s:UserCommand(dir, argv)
3561 if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
3562 let old_sequence_editor = $GIT_SEQUENCE_EDITOR
3563 let $GIT_SEQUENCE_EDITOR = 'true'
3565 let cmd = 'env GIT_SEQUENCE_EDITOR=true ' . cmd
3567 if !empty($GIT_EDITOR) || has('win32')
3568 let old_editor = $GIT_EDITOR
3569 let $GIT_EDITOR = 'false'
3571 let cmd = 'env GIT_EDITOR=false ' . substitute(cmd, '^env ', '', '')
3573 if !has('patch-8.1.0334') && has('terminal') && &autowrite
3574 let autowrite_was_set = 1
3578 let &l:makeprg = cmd
3579 silent noautocmd make!
3580 catch /^Vim\%((\a\+)\)\=:E211/
3581 let err = v:exception
3583 if exists('autowrite_was_set')
3587 let [&l:mp, &l:efm] = [mp, efm]
3588 if exists('old_editor')
3589 let $GIT_EDITOR = old_editor
3591 if exists('old_sequence_editor')
3592 let $GIT_SEQUENCE_EDITOR = old_sequence_editor
3596 call fugitive#ReloadStatus(dir, 1)
3597 if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
3598 if a:cmd =~# '^rebase' &&
3599 \ filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) &&
3600 \ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) &&
3601 \ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^e]'
3603 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1'
3604 elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3606 return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir))
3609 let qflist = getqflist()
3614 let e.pattern = '^<<<<<<<'
3617 call fugitive#Cwindow()
3619 call setqflist(qflist, 'r')
3625 return exists('err') ? 'echoerr '.string(err) : 'exe'
3628 function! s:RebaseClean(file) abort
3629 if !filereadable(a:file)
3632 let old = readfile(a:file)
3634 for i in range(len(new))
3635 let new[i] = substitute(new[i], '^\l\>', '\=get(s:rebase_abbrevs,submatch(0),submatch(0))', '')
3637 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3638 let rebase_shas = getbufvar(a:file, 'fugitive_rebase_shas')
3639 if len(sha) && type(rebase_shas) == type({}) && has_key(rebase_shas, sha)
3640 let new[i] = substitute(new[i], '\C\<' . sha . '\>', rebase_shas[sha], '')
3644 call writefile(new, a:file)
3649 function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort
3650 return s:MergeRebase('merge', a:bang, a:mods, a:args)
3653 function! s:RebaseSubcommand(line1, line2, range, bang, mods, args) abort
3654 return s:MergeRebase('rebase', a:bang, a:mods, a:args)
3657 function! s:PullSubcommand(line1, line2, range, bang, mods, args) abort
3658 return s:MergeRebase('pull', a:bang, a:mods, a:args)
3661 augroup fugitive_merge
3663 autocmd VimLeavePre,BufDelete git-rebase-todo
3664 \ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
3665 \ call s:RebaseClean(expand('<afile>')) |
3666 \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
3667 \ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) |
3670 autocmd BufEnter * nested
3671 \ if exists('s:rebase_continue') |
3672 \ exe s:MergeRebase('rebase', 0, '', [getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort'], remove(s:, 'rebase_continue')) |
3676 call s:command("-nargs=? -bang -complete=customlist,s:MergeComplete Gmerge", "merge")
3677 call s:command("-nargs=? -bang -complete=customlist,s:RebaseComplete Grebase", "rebase")
3678 call s:command("-nargs=? -bang -complete=customlist,s:PullComplete Gpull", "pull")
3680 " Section: :Ggrep, :Glog
3682 if !exists('g:fugitive_summary_format')
3683 let g:fugitive_summary_format = '%s'
3686 function! s:GrepComplete(A, L, P) abort
3687 return s:CompleteSub('grep', a:A, a:L, a:P)
3690 function! s:LogComplete(A, L, P) abort
3691 return s:CompleteSub('log', a:A, a:L, a:P)
3694 function! s:GrepParseLine(prefix, name_only, dir, line) abort
3695 let entry = {'valid': 1}
3696 let match = matchlist(a:line, '^\(.\{-\}\):\(\d\+\):\(\d\+:\)\=\(.*\)$')
3698 let entry.module = match[1]
3699 let entry.lnum = +match[2]
3700 let entry.col = +match[3]
3701 let entry.text = match[4]
3702 elseif a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
3703 return {'text': a:line}
3705 let entry.module = matchstr(a:line, '\CBinary file \zs.*\ze matches$')
3706 if len(entry.module)
3707 let entry.text = 'Binary file'
3711 if empty(entry.module) && a:name_only
3712 let entry.module = a:line
3714 if empty(entry.module)
3715 return {'text': a:line}
3717 if entry.module !~# ':'
3718 let entry.filename = a:prefix . entry.module
3720 let entry.filename = fugitive#Find(entry.module, a:dir)
3725 function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
3728 let listnr = a:line1 == 0 ? a:line1 : a:line2
3729 let cmd = ['--no-pager', 'grep', '-n', '--no-color', '--full-name']
3730 if fugitive#GitVersion(2, 19)
3731 call add(cmd, '--column')
3733 let tree = s:Tree(dir)
3734 if type(a:args) == type([])
3735 let [args, after] = [a:args, '']
3737 let [args, after] = s:SplitExpandChain(a:args, tree)
3739 let prefix = FugitiveVimPath(s:HasOpt(args, '--cached') || empty(tree) ? 'fugitive://' . dir . '//0/' : tree . '/')
3740 let name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
3741 let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
3743 exe listnr 'wincmd w'
3748 call s:QuickfixCreate(listnr, {'title': (listnr < 0 ? ':Ggrep ' : ':Glgrep ') . s:fnameescape(args)})
3749 let tempfile = tempname()
3750 if v:version >= 704 | exe 'silent doautocmd <nomodeline> QuickFixCmdPre ' (listnr < 0 ? 'Ggrep' : 'Glgrep') | endif
3751 exe '!' . escape(s:UserCommand(dir, cmd + args), '%#!')
3752 \ printf(&shellpipe . (&shellpipe =~# '%s' ? '' : ' %s'), s:shellesc(tempfile))
3753 let list = map(readfile(tempfile), 's:GrepParseLine(prefix, name_only, dir, v:val)')
3754 call s:QuickfixSet(listnr, list, 'a')
3755 if v:version >= 704 | exe 'silent doautocmd <nomodeline> QuickFixCmdPost ' (listnr < 0 ? 'Ggrep' : 'Glgrep') | endif
3756 if !has('gui_running')
3759 if !a:bang && !empty(list)
3760 return (listnr < 0 ? 'c' : 'l').'first' . after
3766 function! s:LogFlushQueue(state) abort
3767 let queue = remove(a:state, 'queue')
3768 if a:state.child_found
3769 call remove(queue, 0)
3771 if len(queue) && queue[-1] ==# {'text': ''}
3772 call remove(queue, -1)
3777 function! s:LogParse(state, dir, line) abort
3778 if a:state.context ==# 'hunk' && a:line =~# '^[-+ ]'
3781 let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
3783 let a:state.context = 'commit'
3784 let a:state.base = 'fugitive://' . a:dir . '//' . list[2]
3785 let a:state.base_module = len(list[1]) ? list[1] : list[2]
3786 let a:state.message = list[3]
3787 if has_key(a:state, 'diffing')
3788 call remove(a:state, 'diffing')
3790 let queue = s:LogFlushQueue(a:state)
3791 let a:state.queue = [{
3793 \ 'filename': a:state.base . a:state.target,
3794 \ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
3795 \ 'text': a:state.message}]
3796 let a:state.child_found = 0
3798 elseif type(a:line) == type(0)
3799 return s:LogFlushQueue(a:state)
3800 elseif a:line =~# '^diff'
3801 let a:state.context = 'diffhead'
3802 elseif a:line =~# '^[+-]\{3\} \w/' && a:state.context ==# 'diffhead'
3803 let a:state.diffing = a:line[5:-1]
3804 elseif a:line =~# '^@@[^@]*+\d' && has_key(a:state, 'diffing') && has_key(a:state, 'base')
3805 let a:state.context = 'hunk'
3806 if empty(a:state.target) || a:state.target ==# a:state.diffing
3807 let a:state.child_found = 1
3808 call add(a:state.queue, {
3810 \ 'filename': a:state.base . a:state.diffing,
3811 \ 'module': a:state.base_module . substitute(a:state.diffing, '^/', ':', ''),
3812 \ 'lnum': +matchstr(a:line, '+\zs\d\+'),
3813 \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
3815 elseif a:state.follow &&
3816 \ a:line =~# '^ \%(mode change \d\|\%(create\|delete\) mode \d\|\%(rename\|copy\|rewrite\) .* (\d\+%)$\)'
3817 let rename = matchstr(a:line, '^ rename \zs.* => .*\ze (\d\+%)$')
3819 let rename = rename =~# '{.* => .*}' ? rename : '{' . rename . '}'
3820 if a:state.target ==# simplify('/' . substitute(rename, '{.* => \(.*\)}', '\1', ''))
3821 let a:state.target = simplify('/' . substitute(rename, '{\(.*\) => .*}', '\1', ''))
3824 if !get(a:state, 'ignore_summary')
3825 call add(a:state.queue, {'text': a:line})
3827 elseif a:state.context ==# 'commit' || a:state.context ==# 'init'
3828 call add(a:state.queue, {'text': a:line})
3833 function! s:Log(type, bang, line1, count, args, legacy) abort
3836 let listnr = a:type =~# '^l' ? 0 : -1
3837 let [args, after] = s:SplitExpandChain(a:args, s:Tree(dir))
3838 let split = index(args, '--')
3840 let paths = args[split : -1]
3841 let args = args[0 : split - 1]
3848 if a:line1 == 0 && a:count
3849 let path = fugitive#Path(bufname(a:count), '/', dir)
3851 let path = fugitive#Path(@%, '/', dir)
3857 let state = {'context': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
3858 if path =~# '^/\.git\%(/\|$\)\|^$'
3861 let range = "0," . (a:count ? a:count : bufnr(''))
3862 let extra = ['.' . path]
3863 if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
3864 let state.follow = 1
3865 if !s:HasOpt(args, '--follow')
3866 call insert(args, '--follow')
3868 if !s:HasOpt(args, '--summary')
3869 call insert(args, '--summary')
3870 let state.ignore_summary = 1
3874 if !s:HasOpt(args, '--merges', '--no-merges')
3875 call insert(args, '--no-merges')
3877 call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
3879 if len(path) && empty(filter(copy(args), 'v:val =~# "^[^-]"'))
3880 let owner = s:Owner(@%, dir)
3882 call add(args, owner)
3888 if s:HasOpt(args, '-g', '--walk-reflogs')
3889 let format = "%gd\t%H %gs"
3891 let format = "%h\t%H " . g:fugitive_summary_format
3893 let cmd = ['--no-pager']
3894 if fugitive#GitVersion(1, 9)
3895 call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'])
3897 call extend(cmd, ['log', '-U0', '--no-patch'])
3900 \ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
3901 \ args + paths + extra)
3902 let state.target = path
3903 let title = (listnr < 0 ? ':Gclog ' : ':Gllog ') . s:fnameescape(args + paths)
3904 if empty(paths + extra) && a:legacy && len(s:Relative('/'))
3905 let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after
3907 return s:QuickfixStream(listnr, title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after
3910 call s:command("-bang -nargs=? -range=-1 -addr=windows -complete=customlist,s:GrepComplete Ggrep", "grep")
3911 call s:command("-bang -nargs=? -complete=customlist,s:GrepComplete Gcgrep :execute s:GrepSubcommand(-1, -1, 0, <bang>0, '<mods>', <q-args>)")
3912 call s:command("-bang -nargs=? -complete=customlist,s:GrepComplete Glgrep :execute s:GrepSubcommand(0, 0, 0, <bang>0, '<mods>', <q-args>)")
3913 call s:command("-bang -nargs=? -range=-1 -addr=other -complete=customlist,s:LogComplete Glog :exe s:Log('c',<bang>0,<line1>,<count>,<q-args>, 1)")
3914 call s:command("-bang -nargs=? -range=-1 -addr=other -complete=customlist,s:LogComplete Gclog :exe s:Log('c',<bang>0,<line1>,<count>,<q-args>, 0)")
3915 call s:command("-bang -nargs=? -range=-1 -addr=other -complete=customlist,s:LogComplete Gllog :exe s:Log('l',<bang>0,<line1>,<count>,<q-args>, 0)")
3917 " Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
3919 function! s:UsableWin(nr) abort
3920 return a:nr && !getwinvar(a:nr, '&previewwindow') && !getwinvar(a:nr, '&winfixwidth') &&
3921 \ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
3922 \ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
3923 \ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
3926 function! s:OpenParse(args) abort
3928 let args = copy(a:args)
3929 while !empty(args) && args[0] =~# '^+'
3930 call add(pre, ' ' . escape(remove(args, 0), ' |"'))
3933 let file = join(args)
3934 elseif empty(expand('%'))
3936 elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>'
3941 return [s:Expand(file), join(pre)]
3944 function! s:DiffClose() abort
3945 let mywinnr = winnr()
3946 for winnr in [winnr('#')] + range(winnr('$'),1,-1)
3947 if winnr != mywinnr && getwinvar(winnr,'&diff')
3948 execute winnr.'wincmd w'
3958 function! s:BlurStatus() abort
3959 if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
3960 let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
3962 exe winnrs[0].'wincmd w'
3972 function! s:OpenExec(cmd, mods, args, ...) abort
3973 let dir = a:0 ? s:Dir(a:1) : s:Dir()
3974 let temp = tempname()
3975 let columns = get(g:, 'fugitive_columns', 80)
3979 let env = 'set COLUMNS=' . columns . '& '
3981 let env = 'env COLUMNS=' . columns . ' '
3983 silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
3984 \ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
3986 let temp = s:Resolve(temp)
3987 let first = join(readfile(temp, '', 2), "\n")
3988 if first =~# '\<\([[:upper:][:digit:]_-]\+(\d\+)\).*\1'
3989 let filetype = 'man'
3991 let filetype = 'git'
3993 let s:temp_files[s:cpath(temp)] = { 'dir': dir, 'filetype': filetype, 'modifiable': first =~# '^diff ' }
3997 silent execute s:Mods(a:mods) . a:cmd temp
3998 call fugitive#ReloadStatus(dir, 1)
3999 return 'echo ' . string(':!' . s:UserCommand(dir, a:args))
4002 function! s:Open(cmd, bang, mods, arg, args) abort
4004 return s:OpenExec(a:cmd, a:mods, s:SplitExpand(a:arg, s:Tree()))
4008 let mods = s:Mods(a:mods)
4010 let [file, pre] = s:OpenParse(a:args)
4011 let file = s:Generate(file)
4013 return 'echoerr ' . string(v:exception)
4015 if file !~# '^\a\a\+:'
4016 let file = s:sub(file, '/$', '')
4021 return mods . a:cmd . pre . ' ' . s:fnameescape(file)
4024 function! s:ReadCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
4025 let mods = s:Mods(a:mods)
4028 let delete = 'silent 1,' . line('$') . 'delete_|'
4029 let after = line('$')
4031 let delete = 'silent ' . a:line1 . ',' . a:count . 'delete_|'
4037 let args = s:SplitExpand(a:arg, s:Tree(dir))
4038 silent execute mods . after . 'read!' escape(s:UserCommand(dir, ['--no-pager'] + args), '!#%')
4039 execute delete . 'diffupdate'
4040 call fugitive#ReloadStatus()
4041 return 'redraw|echo '.string(':!'.s:UserCommand(dir, args))
4045 let [file, pre] = s:OpenParse(a:args)
4046 let file = s:Generate(file)
4048 return 'echoerr ' . string(v:exception)
4050 if file =~# '^fugitive:' && after is# 0
4051 return 'exe ' .string(mods . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
4054 exe after . 'foldopen!'
4056 return mods . after . 'read' . pre . ' ' . s:fnameescape(file) . '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')
4059 function! s:ReadComplete(A,L,P) abort
4061 return fugitive#CompleteGit(a:A, a:L, a:P)
4063 return fugitive#CompleteObject(a:A, a:L, a:P)
4067 call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Ge execute s:Open('edit<bang>', 0, '<mods>', <q-args>, [<f-args>])")
4068 call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gedit execute s:Open('edit<bang>', 0, '<mods>', <q-args>, [<f-args>])")
4069 call s:command("-bar -bang -nargs=* -complete=customlist,s:ReadComplete Gpedit execute s:Open('pedit', <bang>0, '<mods>', <q-args>, [<f-args>])")
4070 call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete Gsplit execute s:Open((<count> > 0 ? <count> : '').(<count> ? 'split' : 'edit'), <bang>0, '<mods>', <q-args>, [<f-args>])")
4071 call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete Gvsplit execute s:Open((<count> > 0 ? <count> : '').(<count> ? 'vsplit' : 'edit!'), <bang>0, '<mods>', <q-args>, [<f-args>])")
4072 call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete -addr=tabs Gtabedit execute s:Open((<count> >= 0 ? <count> : '').'tabedit', <bang>0, '<mods>', <q-args>, [<f-args>])")
4073 call s:command("-bar -bang -nargs=* -range=-1 -complete=customlist,s:ReadComplete Gread", "Read")
4075 " Section: :Gwrite, :Gwq
4077 call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gwrite", "Write")
4078 call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gw", "Write")
4079 call s:command("-bar -bang -nargs=* -complete=customlist,fugitive#CompleteObject Gwq", "Wq")
4081 function! s:WriteCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
4083 if exists('b:fugitive_commit_arguments')
4084 return 'write|bdelete'
4085 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
4087 elseif get(b:, 'fugitive_type', '') ==# 'index'
4089 elseif &buftype ==# 'nowrite' && getline(4) =~# '^+++ '
4090 let filename = getline(4)[6:-1]
4093 setlocal buftype=nowrite
4094 if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6]
4095 let [message, exec_error] = s:ChompError(['apply', '--cached', '--reverse', '--', expand('%:p')])
4097 let [message, exec_error] = s:ChompError(['apply', '--cached', '--', expand('%:p')])
4107 return 'Gedit '.fnameescape(filename)
4110 let mytab = tabpagenr()
4111 let mybufnr = bufnr('')
4113 let file = len(a:args) ? s:Generate(s:Expand(join(a:args, ' '))) : fugitive#Real(@%)
4115 return 'echoerr ' . string(v:exception)
4118 return 'echoerr '.string('fugitive: cannot determine file path')
4120 if file =~# '^fugitive:'
4121 return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
4123 let always_permitted = s:cpath(fugitive#Real(@%), file) && empty(s:DirCommitFile(@%)[1])
4124 if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
4125 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
4126 return 'echoerr v:errmsg'
4129 for nr in range(1,bufnr('$'))
4130 if fnamemodify(bufname(nr),':p') ==# file
4135 if treebufnr > 0 && treebufnr != bufnr('')
4136 let temp = tempname()
4137 silent execute 'keepalt %write '.temp
4138 for tab in [mytab] + range(1,tabpagenr('$'))
4139 for winnr in range(1,tabpagewinnr(tab,'$'))
4140 if tabpagebuflist(tab)[winnr-1] == treebufnr
4141 execute 'tabnext '.tab
4143 execute winnr.'wincmd w'
4144 let restorewinnr = 1
4147 let lnum = line('.')
4148 let last = line('$')
4149 silent execute '$read '.temp
4150 silent execute '1,'.last.'delete_'
4156 if exists('restorewinnr')
4159 execute 'tabnext '.mytab
4166 call writefile(readfile(temp,'b'),file,'b')
4169 execute 'write! '.s:fnameescape(file)
4173 let [error, exec_error] = s:ChompError(['add', '--force', '--', file])
4175 let [error, exec_error] = s:ChompError(['add', '--', file])
4178 let v:errmsg = 'fugitive: '.error
4179 return 'echoerr v:errmsg'
4181 if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
4185 let one = s:Generate(':1:'.file)
4186 let two = s:Generate(':2:'.file)
4187 let three = s:Generate(':3:'.file)
4188 for nr in range(1,bufnr('$'))
4189 let name = fnamemodify(bufname(nr), ':p')
4190 if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three)
4191 execute nr.'bdelete'
4196 let zero = s:Generate(':0:'.file)
4197 silent execute 'doautocmd' s:nomodeline 'BufWritePost' s:fnameescape(zero)
4198 for tab in range(1,tabpagenr('$'))
4199 for winnr in range(1,tabpagewinnr(tab,'$'))
4200 let bufnr = tabpagebuflist(tab)[winnr-1]
4201 let bufname = fnamemodify(bufname(bufnr), ':p')
4202 if bufname ==# zero && bufnr != mybufnr
4203 execute 'tabnext '.tab
4205 execute winnr.'wincmd w'
4206 let restorewinnr = 1
4209 let lnum = line('.')
4210 let last = line('$')
4211 silent execute '$read '.s:fnameescape(file)
4212 silent execute '1,'.last.'delete_'
4217 if exists('restorewinnr')
4220 execute 'tabnext '.mytab
4226 call fugitive#ReloadStatus()
4230 function! s:WqCommand(...) abort
4231 let bang = a:5 ? '!' : ''
4232 if exists('b:fugitive_commit_arguments')
4235 let result = call(s:function('s:WriteCommand'),a:000)
4236 if result =~# '^\%(write\|wq\|echoerr\)'
4237 return s:sub(result,'^write','wq')
4239 return result.'|quit'.bang
4243 augroup fugitive_commit
4245 autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute substitute(s:FinishCommit(), '\C^echoerr \(''[^'']*''\)*', 'redraw|echohl ErrorMsg|echo \1|echohl NONE', '')
4248 " Section: :Gpush, :Gfetch
4250 function! s:PushComplete(A, L, P) abort
4251 return s:CompleteSub('push', a:A, a:L, a:P, function('s:CompleteRemote'))
4254 function! s:FetchComplete(A, L, P) abort
4255 return s:CompleteSub('fetch', a:A, a:L, a:P, function('s:CompleteRemote'))
4258 function! s:AskPassArgs(dir) abort
4259 if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) &&
4260 \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#Config('core.askPass', a:dir))
4261 if s:executable(s:ExecPath() . '/git-gui--askpass')
4262 return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass']
4263 elseif s:executable('ssh-askpass')
4264 return ['-c', 'core.askPass=ssh-askpass']
4270 function! s:Dispatch(bang, cmd, args) abort
4272 let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
4274 let b:current_compiler = 'git'
4275 let &l:errorformat = s:common_efm
4276 let &l:makeprg = s:UserCommand(dir, s:AskPassArgs(dir) + [a:cmd] + a:args)
4277 if exists(':Make') == 2
4281 if !has('patch-8.1.0334') && has('terminal') && &autowrite
4282 let autowrite_was_set = 1
4286 silent noautocmd make!
4288 return 'call fugitive#Cwindow()|call fugitive#ReloadStatus()'
4291 let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
4292 if empty(cc) | unlet! b:current_compiler | endif
4293 if exists('autowrite_was_set')
4299 function! s:PushSubcommand(line1, line2, range, bang, mods, args) abort
4300 return s:Dispatch(a:bang ? '!' : '', 'push', a:args)
4303 function! s:FetchSubcommand(line1, line2, range, bang, mods, args) abort
4304 return s:Dispatch(a:bang ? '!' : '', 'fetch', a:args)
4307 call s:command("-nargs=? -bang -complete=customlist,s:PushComplete Gpush", "push")
4308 call s:command("-nargs=? -bang -complete=customlist,s:FetchComplete Gfetch", "fetch")
4312 call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gdiffsplit :execute s:Diff(1, <bang>0, '<mods>', <f-args>)")
4313 call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Gvdiffsplit :execute s:Diff(0, <bang>0, 'vertical <mods>', <f-args>)")
4314 call s:command("-bang -bar -nargs=* -complete=customlist,fugitive#CompleteObject Ghdiffsplit :execute s:Diff(0, <bang>0, '<mods>', <f-args>)")
4316 augroup fugitive_diff
4318 autocmd BufWinLeave *
4319 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
4320 \ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
4322 autocmd BufWinEnter *
4323 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 1 |
4324 \ call s:diffoff() |
4328 function! s:can_diffoff(buf) abort
4329 return getwinvar(bufwinnr(a:buf), '&diff') &&
4330 \ !empty(s:Dir(a:buf)) &&
4331 \ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
4334 function! fugitive#CanDiffoff(buf) abort
4335 return s:can_diffoff(bufnr(a:buf))
4338 function! s:diff_modifier(count) abort
4339 let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
4340 if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
4342 elseif &diffopt =~# 'vertical'
4344 elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
4351 function! s:diff_window_count() abort
4353 for nr in range(1,winnr('$'))
4354 let c += getwinvar(nr,'&diff')
4359 function! s:diff_restore() abort
4360 let restore = 'setlocal nodiff noscrollbind'
4361 \ . ' scrollopt=' . &l:scrollopt
4362 \ . (&l:wrap ? ' wrap' : ' nowrap')
4363 \ . ' foldlevel=999'
4364 \ . ' foldmethod=' . &l:foldmethod
4365 \ . ' foldcolumn=' . &l:foldcolumn
4366 \ . ' foldlevel=' . &l:foldlevel
4367 \ . (&l:foldenable ? ' foldenable' : ' nofoldenable')
4368 if has('cursorbind')
4369 let restore .= (&l:cursorbind ? ' ' : ' no') . 'cursorbind'
4374 function! s:diffthis() abort
4376 let w:fugitive_diff_restore = s:diff_restore()
4381 function! s:diffoff() abort
4382 if exists('w:fugitive_diff_restore')
4383 execute w:fugitive_diff_restore
4384 unlet w:fugitive_diff_restore
4390 function! s:diffoff_all(dir) abort
4391 let curwin = winnr()
4392 for nr in range(1,winnr('$'))
4393 if getwinvar(nr,'&diff')
4395 execute nr.'wincmd w'
4396 let restorewinnr = 1
4398 if s:Dir() ==# a:dir
4403 execute curwin.'wincmd w'
4406 function! s:CompareAge(mine, theirs) abort
4407 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
4408 let mine = substitute(a:mine, '^:', '', '')
4409 let theirs = substitute(a:theirs, '^:', '', '')
4410 let my_score = get(scores, ':'.mine, 0)
4411 let their_score = get(scores, ':'.theirs, 0)
4412 if my_score || their_score
4413 return my_score < their_score ? -1 : my_score != their_score
4414 elseif mine ==# theirs
4417 let base = s:TreeChomp('merge-base', mine, theirs)
4420 elseif base ==# theirs
4423 let my_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:mine, '--')
4424 let their_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:theirs, '--')
4425 return my_time < their_time ? -1 : my_time != their_time
4428 function! s:IsConflicted() abort
4429 return len(@%) && !empty(s:ChompDefault('', 'ls-files', '--unmerged', '--', expand('%:p')))
4432 function! s:Diff(autodir, keepfocus, mods, ...) abort
4433 if exists(':DiffGitCached') && !a:0
4434 return s:Mods(a:mods) . 'DiffGitCached'
4437 let args = copy(a:000)
4439 if get(args, 0) =~# '^+'
4440 let post = remove(args, 0)[1:-1]
4442 let commit = s:DirCommitFile(@%)[1]
4443 if a:mods =~# '\<tab\>'
4444 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4447 let mods = 'keepalt ' . a:mods
4449 let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
4450 if (empty(args) || args[0] ==# ':') && a:keepfocus
4451 if empty(commit) && s:IsConflicted()
4452 let parents = [s:Relative(':2:'), s:Relative(':3:')]
4453 elseif empty(commit)
4454 let parents = [s:Relative(':0:')]
4455 elseif commit =~# '^\d\=$'
4456 let parents = [s:Relative('HEAD:')]
4457 elseif commit =~# '^\x\x\+$'
4458 let parents = s:LinesError(['rev-parse', commit . '^@'])[0]
4459 call map(parents, 's:Relative(v:val . ":")')
4463 if exists('parents') && len(parents) > 1
4464 let mods = (a:autodir ? s:diff_modifier(len(parents) + 1) : '') . s:Mods(mods, 'leftabove')
4466 execute mods 'split' s:fnameescape(s:Generate(parents[0]))
4467 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4471 call s:Map('n', 'd2o', ':diffget '.nr2.'<Bar>diffupdate<CR>', '<silent>')
4472 let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
4473 for i in range(len(parents)-1, 1, -1)
4474 execute mods 'split' s:fnameescape(s:Generate(parents[i]))
4475 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4479 call s:Map('n', 'd' . (i + 2) . 'o', ':diffget '.nrx.'<Bar>diffupdate<CR>', '<silent>')
4487 let arg = join(args, ' ')
4491 let file = s:Relative()
4493 let file = s:Relative(':0:')
4494 elseif arg =~# '^:\d$'
4495 let file = s:Relative(arg . ':')
4498 let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
4500 return 'echoerr ' . string(v:exception)
4503 if file !~# ':' && file !~# '^/' && s:TreeChomp('cat-file','-t',file) =~# '^\%(tag\|commit\)$'
4504 let file = file.s:Relative(':')
4506 elseif exists('parents') && len(parents)
4507 let file = parents[-1]
4509 let file = s:Relative()
4510 elseif s:IsConflicted()
4511 let file = s:Relative(':1:')
4512 let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
4514 let file = s:Relative(':0:')
4516 let spec = s:Generate(file)
4517 let restore = s:diff_restore()
4518 let w:fugitive_diff_restore = restore
4519 if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
4520 let mods = s:Mods(mods, 'rightbelow')
4522 let mods = s:Mods(mods, 'leftabove')
4524 let mods = (a:autodir ? s:diff_modifier(2) : '') . mods
4525 if &diffopt =~# 'vertical'
4526 let diffopt = &diffopt
4527 set diffopt-=vertical
4529 execute mods 'diffsplit' s:fnameescape(spec)
4530 let &l:readonly = &l:readonly
4532 let w:fugitive_diff_restore = restore
4534 if getwinvar('#', '&diff')
4537 call feedkeys(winnr."\<C-W>w", 'n')
4542 return 'echoerr ' . string(v:exception)
4544 if exists('diffopt')
4545 let &diffopt = diffopt
4550 " Section: :Gmove, :Gremove
4552 function! s:Move(force, rename, destination) abort
4553 if a:destination =~# '^\.\.\=\%(/\|$\)'
4554 let destination = simplify(getcwd() . '/' . a:destination)
4555 elseif a:destination =~# '^\a\+:\|^/'
4556 let destination = a:destination
4557 elseif a:destination =~# '^:/:\='
4558 let destination = s:Tree() . substitute(a:destination, '^:/:\=', '', '')
4559 elseif a:destination =~# '^:(\%(top\|top,literal\|literal,top\))'
4560 let destination = s:Tree() . matchstr(a:destination, ')\zs.*')
4561 elseif a:destination =~# '^:(literal)'
4562 let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
4564 let destination = expand('%:p:s?[\/]$??:h') . '/' . a:destination
4566 let destination = s:Tree() . '/' . a:destination
4568 let destination = s:Slash(destination)
4572 let [message, exec_error] = s:ChompError(['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination])
4574 let v:errmsg = 'fugitive: '.message
4575 return 'echoerr v:errmsg'
4577 if isdirectory(destination)
4578 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
4580 call fugitive#ReloadStatus()
4581 if empty(s:DirCommitFile(@%)[1])
4582 if isdirectory(destination)
4583 return 'keepalt edit '.s:fnameescape(destination)
4585 return 'keepalt saveas! '.s:fnameescape(destination)
4588 return 'file '.s:fnameescape(s:Generate(':0:'.destination))
4592 function! s:RenameComplete(A,L,P) abort
4593 if a:A =~# '^[.:]\=/'
4594 return fugitive#CompletePath(a:A)
4596 let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
4597 return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
4601 function! s:Remove(after, force) abort
4602 if s:DirCommitFile(@%)[1] ==# ''
4604 elseif s:DirCommitFile(@%)[1] ==# '0'
4605 let cmd = ['rm','--cached']
4607 let v:errmsg = 'fugitive: rm not supported here'
4608 return 'echoerr v:errmsg'
4611 let cmd += ['--force']
4613 let [message, exec_error] = s:ChompError(cmd + ['--', expand('%:p')])
4615 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
4616 return 'echoerr '.string(v:errmsg)
4618 call fugitive#ReloadStatus()
4619 return a:after . (a:force ? '!' : '')
4623 augroup fugitive_remove
4625 autocmd User Fugitive if s:DirCommitFile(@%)[1] =~# '^0\=$' |
4626 \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,fugitive#CompletePath Gmove :execute s:Move(<bang>0,0,<q-args>)" |
4627 \ exe "command! -buffer -bar -bang -nargs=1 -complete=customlist,s:RenameComplete Grename :execute s:Move(<bang>0,1,<q-args>)" |
4628 \ exe "command! -buffer -bar -bang Gremove :execute s:Remove('edit',<bang>0)" |
4629 \ exe "command! -buffer -bar -bang Gdelete :execute s:Remove('bdelete',<bang>0)" |
4635 function! s:Keywordprg() abort
4636 let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
4637 if has('gui_running') && !has('win32')
4638 return s:UserCommand() . ' --no-pager' . args . ' log -1'
4640 return s:UserCommand() . args . ' show'
4644 function! s:linechars(pattern) abort
4645 let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
4646 if exists('*synconcealed') && &conceallevel > 1
4647 for col in range(1, chars)
4648 let chars -= synconcealed(line('.'), col)[0]
4654 function! s:BlameBufnr(...) abort
4655 let state = s:TempState(bufname(a:0 ? a:1 : ''))
4656 if get(state, 'filetype', '') ==# 'fugitiveblame'
4657 return get(state, 'bufnr', -1)
4663 function! s:BlameCommitFileLnum(...) abort
4664 let line = a:0 ? a:1 : getline('.')
4665 let state = a:0 ? a:2 : s:TempState()
4666 let commit = matchstr(line, '^\^\=\zs\x\+')
4667 if commit =~# '^0\+$'
4669 elseif line !~# '^\^' && has_key(state, 'blame_reverse_end')
4670 let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '')
4672 let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
4673 let path = matchstr(line, '^\^\=\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)')
4674 if empty(path) && lnum
4675 let path = get(state, 'blame_file', '')
4677 return [commit, path, lnum]
4680 function! s:BlameLeave() abort
4681 let bufwinnr = bufwinnr(s:BlameBufnr())
4683 let bufnr = bufnr('')
4684 exe bufwinnr . 'wincmd w'
4685 return bufnr . 'bdelete'
4690 function! s:BlameQuit() abort
4691 let cmd = s:BlameLeave()
4694 elseif len(s:DirCommitFile(@%)[1])
4695 return cmd . '|Gedit'
4701 function! s:BlameComplete(A, L, P) abort
4702 return s:CompleteSub('blame', a:A, a:L, a:P)
4705 function! s:BlameSubcommand(line1, count, range, bang, mods, args) abort
4707 let flags = copy(a:args)
4713 if a:line1 > 0 && a:count > 0 && a:range != 1
4714 call extend(ranges, ['-L', a:line1 . ',' . a:count])
4716 while i < len(flags)
4717 let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
4718 if len(match) && len(match[2])
4719 call insert(flags, match[1])
4720 let flags[i+1] = '-' . match[2]
4724 if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$'
4726 elseif arg ==# '--contents' && i + 1 < len(flags)
4727 call extend(commits, remove(flags, i, i+1))
4729 elseif arg ==# '-L' && i + 1 < len(flags)
4730 call extend(ranges, remove(flags, i, i+1))
4732 elseif arg =~# '^--contents='
4733 call add(commits, remove(flags, i))
4735 elseif arg =~# '^-L.'
4736 call add(ranges, remove(flags, i))
4738 elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\)$'
4742 echo s:ChompError(['blame', arg])[0]
4747 if i + 1 < len(flags)
4748 call extend(files, remove(flags, i + 1, -1))
4750 call remove(flags, i)
4752 elseif arg !~# '^-' && (s:HasOpt(flags, '--not') || arg !~# '^\^')
4753 if index(flags, '--') >= 0
4754 call add(commits, remove(flags, i))
4757 if arg =~# '\.\.' && arg !~# '^\.\.\=\%(/\|$\)' && empty(commits)
4758 call add(commits, remove(flags, i))
4762 let dcf = s:DirCommitFile(fugitive#Find(arg))
4763 if len(dcf[1]) && empty(dcf[2])
4764 call add(commits, remove(flags, i))
4769 call add(files, remove(flags, i))
4774 if empty(ranges + commits + files) && has_key(s:TempState(), 'blame_flags')
4775 return substitute(s:BlameLeave(), '^$', 'bdelete', '')
4777 let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./'))), '^\.\%(/\|$\)', '', '')
4778 if empty(commits) && len(files) > 1
4779 call add(commits, remove(files, 1))
4782 let cmd = ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', 'blame', '--show-number']
4783 call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
4784 if a:count > 0 && empty(ranges)
4785 let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
4787 call extend(cmd, ranges)
4790 elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$'))
4791 let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')]
4792 elseif empty(files) && !s:HasOpt(flags, '--reverse')
4793 let cmd += ['--contents', '-']
4795 let basecmd = escape(fugitive#Prepare(cmd) . ' -- ' . s:shellesc(len(files) ? files : file), '!#%')
4796 let tempname = tempname()
4797 let error = tempname . '.err'
4798 let temp = tempname . (raw ? '' : '.fugitiveblame')
4800 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
4802 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
4807 let lines = readfile(error)
4809 let lines = readfile(temp)
4811 for i in range(len(lines))
4812 if lines[i] =~# '^error: \|^fatal: '
4820 if i != len(lines) - 1
4826 let temp_state = {'dir': s:Dir(), 'filetype': (raw ? '' : 'fugitiveblame'), 'blame_flags': flags, 'blame_file': file, 'modifiable': 0}
4827 if s:HasOpt(flags, '--reverse')
4828 let temp_state.blame_reverse_end = matchstr(get(commits, 0, ''), '\.\.\zs.*')
4830 if (a:line1 == 0 || a:range == 1) && a:count > 0
4831 let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split')
4832 return s:BlameCommit(edit, get(readfile(temp), 0, ''), temp_state)
4834 let temp = s:Resolve(temp)
4835 let s:temp_files[s:cpath(temp)] = temp_state
4836 if len(ranges + commits + files) || raw
4837 let mods = s:Mods(a:mods)
4839 exe 'silent keepalt' mods 'split' s:fnameescape(temp)
4840 elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden)
4841 exe 'silent' mods 'edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp)
4843 return mods . 'edit ' . s:fnameescape(temp)
4847 if a:mods =~# '\<tab\>'
4850 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4851 for winnr in range(winnr('$'),1,-1)
4852 if getwinvar(winnr, '&scrollbind')
4853 call setwinvar(winnr, '&scrollbind', 0)
4855 if exists('+cursorbind') && getwinvar(winnr, '&cursorbind')
4856 call setwinvar(winnr, '&cursorbind', 0)
4858 if s:BlameBufnr(winbufnr(winnr)) > 0
4859 execute winbufnr(winnr).'bdelete'
4862 let bufnr = bufnr('')
4863 let temp_state.bufnr = bufnr
4864 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
4865 if exists('+cursorbind')
4866 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
4869 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
4872 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
4874 setlocal scrollbind nowrap nofoldenable
4875 if exists('+cursorbind')
4878 let top = line('w0') + &scrolloff
4879 let current = line('.')
4880 exe 'silent keepalt' (a:bang ? s:Mods(mods) . 'split' : s:Mods(mods, 'leftabove') . 'vsplit') s:fnameescape(temp)
4881 let w:fugitive_leave = restore
4885 if exists('+cursorbind')
4888 setlocal nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth
4889 if exists('+relativenumber')
4890 setlocal norelativenumber
4892 execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
4893 call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>')
4894 call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>')
4895 call s:Map('n', 'D', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze\\d\\ze\\s\\+\\d\\+)')+1-v:count)<CR>", '<silent>')
4902 return 'echoerr ' . string(v:exception)
4906 function! s:BlameCommit(cmd, ...) abort
4907 let line = a:0 ? a:1 : getline('.')
4908 let state = a:0 ? a:2 : s:TempState()
4909 let sigil = has_key(state, 'blame_reverse_end') ? '-' : '+'
4910 let mods = (s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ")
4911 let [commit, path, lnum] = s:BlameCommitFileLnum(line, state)
4912 if empty(commit) && len(path) && has_key(state, 'blame_reverse_end')
4913 let path = (len(state.blame_reverse_end) ? state.blame_reverse_end . ':' : ':(top)') . path
4914 return s:Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4916 if commit =~# '^0*$'
4917 return 'echoerr ' . string('fugitive: no commit')
4919 if line =~# '^\^' && !has_key(state, 'blame_reverse_end')
4920 let path = commit . ':' . path
4921 return s:Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4923 let cmd = s:Open(mods . a:cmd, 0, '', commit, [commit])
4924 if cmd =~# '^echoerr'
4928 if a:cmd ==# 'pedit' || empty(path)
4931 if search('^diff .* b/\M'.escape(path,'\').'$','W')
4933 let head = line('.')
4934 while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
4935 let top = +matchstr(getline('.'),' ' . sigil .'\zs\d\+')
4936 let len = +matchstr(getline('.'),' ' . sigil . '\d\+,\zs\d\+')
4937 if lnum >= top && lnum <= top + len
4938 let offset = lnum - top
4946 while offset > 0 && line('.') < line('$')
4948 if getline('.') =~# '^[ ' . sigil . ']'
4961 function! s:BlameJump(suffix, ...) abort
4962 let suffix = a:suffix
4963 let [commit, path, lnum] = s:BlameCommitFileLnum()
4965 return 'echoerr ' . string('fugitive: could not determine filename for blame')
4967 if commit =~# '^0*$'
4971 let offset = line('.') - line('w0')
4972 let flags = get(s:TempState(), 'blame_flags', [])
4974 if s:HasOpt(flags, '--reverse')
4975 call remove(flags, '--reverse')
4977 call add(flags, '--reverse')
4980 let blame_bufnr = s:BlameBufnr()
4982 let bufnr = bufnr('')
4983 let winnr = bufwinnr(blame_bufnr)
4985 exe winnr.'wincmd w'
4987 execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
4993 if exists(':Gblame')
4994 let my_bufnr = bufnr('')
4996 let blame_args = flags + [commit . suffix, '--', path]
4997 let result = s:BlameSubcommand(0, 0, 0, 0, '', blame_args)
4999 let blame_args = flags
5000 let result = s:BlameSubcommand(-1, -1, 0, 0, '', blame_args)
5002 if bufnr('') == my_bufnr
5007 let delta = line('.') - line('w0') - offset
5009 execute 'normal! '.delta."\<C-E>"
5011 execute 'normal! '.(-delta)."\<C-Y>"
5015 echo ':Gblame' s:fnameescape(blame_args)
5020 let s:hash_colors = {}
5022 function! fugitive#BlameSyntax() abort
5023 let conceal = has('conceal') ? ' conceal' : ''
5024 let config = fugitive#Config()
5025 let flags = get(s:TempState(), 'blame_flags', [])
5026 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
5027 syn match FugitiveblameHash "\%(^\^\=\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5028 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5029 if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$' || s:HasOpt(flags, '-b')
5030 syn match FugitiveblameBoundaryIgnore "^\^\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5032 syn match FugitiveblameBoundary "^\^"
5034 syn match FugitiveblameScoreDebug " *\d\+\s\+\d\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile contained skipwhite
5035 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
5036 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
5037 exec 'syn match FugitiveblameLineNumber "\s*\d\+)\@=" contained containedin=FugitiveblameAnnotation' conceal
5038 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)
5039 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5040 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5041 syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
5042 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
5043 hi def link FugitiveblameBoundary Keyword
5044 hi def link FugitiveblameHash Identifier
5045 hi def link FugitiveblameBoundaryIgnore Ignore
5046 hi def link FugitiveblameUncommitted Ignore
5047 hi def link FugitiveblameScoreDebug Debug
5048 hi def link FugitiveblameTime PreProc
5049 hi def link FugitiveblameLineNumber Number
5050 hi def link FugitiveblameOriginalFile String
5051 hi def link FugitiveblameOriginalLineNumber Float
5052 hi def link FugitiveblameShort FugitiveblameDelimiter
5053 hi def link FugitiveblameDelimiter Delimiter
5054 hi def link FugitiveblameNotCommittedYet Comment
5055 if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
5059 for lnum in range(1, line('$'))
5060 let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
5061 if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
5065 if &t_Co > 16 && get(g:, 'CSApprox_loaded') && !empty(findfile('autoload/csapprox/per_component.vim', escape(&rtp, ' ')))
5066 \ && empty(get(s:hash_colors, hash))
5067 let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
5068 let color = csapprox#per_component#Approximate(r, g, b)
5069 if color == 16 && &background ==# 'dark'
5072 let s:hash_colors[hash] = ' ctermfg='.color
5074 let s:hash_colors[hash] = ''
5076 exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
5078 call s:BlameRehighlight()
5081 function! s:BlameRehighlight() abort
5082 for [hash, cterm] in items(s:hash_colors)
5083 if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
5084 exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
5086 exe 'hi link FugitiveblameHash'.hash.' Identifier'
5091 function! s:BlameFileType() abort
5093 setlocal foldmethod=manual
5095 let &l:keywordprg = s:Keywordprg()
5097 let b:undo_ftplugin = 'setl keywordprg= foldmethod<'
5098 if exists('+concealcursor')
5099 setlocal concealcursor=nc conceallevel=2
5100 let b:undo_ftplugin .= ' concealcursor< conceallevel<'
5105 call s:Map('n', '<F1>', ':help fugitive-:Gblame<CR>', '<silent>')
5106 call s:Map('n', 'g?', ':help fugitive-:Gblame<CR>', '<silent>')
5107 if mapcheck('q', 'n') =~# '^$\|bdelete'
5108 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>')
5110 call s:Map('n', 'gq', ':exe <SID>BlameQuit()<CR>', '<silent>')
5111 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5112 call s:Map('n', '<CR>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5113 call s:Map('n', '-', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>')
5114 call s:Map('n', 'P', ':<C-U>exe <SID>BlameJump("^".v:count1)<CR>', '<silent>')
5115 call s:Map('n', '~', ':<C-U>exe <SID>BlameJump("~".v:count1)<CR>', '<silent>')
5116 call s:Map('n', 'i', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5117 call s:Map('n', 'o', ':<C-U>exe <SID>BlameCommit("split")<CR>', '<silent>')
5118 call s:Map('n', 'O', ':<C-U>exe <SID>BlameCommit("tabedit")<CR>', '<silent>')
5119 call s:Map('n', 'p', ':<C-U>exe <SID>BlameCommit("pedit")<CR>', '<silent>')
5122 augroup fugitive_blame
5124 autocmd FileType fugitiveblame call s:BlameFileType()
5125 autocmd ColorScheme,GUIEnter * call s:BlameRehighlight()
5126 autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
5129 call s:command('-buffer -bang -range=-1 -nargs=? -complete=customlist,s:BlameComplete Gblame', 'blame')
5133 call s:command("-bar -bang -range=-1 -nargs=* -complete=customlist,fugitive#CompleteObject Gbrowse", "Browse")
5135 let s:redirects = {}
5137 function! s:BrowseCommand(line1, line2, range, count, bang, mods, reg, arg, args) abort
5141 let validremote = '\.\|\.\=/.*\|[[:alnum:]_-]\+\%(://.\{-\}\)\='
5144 return 'echoerr ' . string('fugitive: ''-'' no longer required to get persistent URL if range given')
5146 return 'echoerr ' . string('fugitive: use :0Gbrowse instead of :Gbrowse -')
5149 let remote = matchstr(join(a:args, ' '),'@\zs\%('.validremote.'\)$')
5150 let rev = substitute(join(a:args, ' '),'@\%('.validremote.'\)$','','')
5156 let rev = s:DirRev(@%)[1]
5159 let expanded = s:Relative()
5161 let expanded = s:Expand(rev)
5163 let cdir = FugitiveVimPath(fugitive#CommonDir(s:Dir()))
5164 for subdir in ['tags/', 'heads/', 'remotes/']
5165 if expanded !~# '^[./]' && filereadable(cdir . '/refs/' . subdir . expanded)
5166 let expanded = '.git/refs/' . subdir . expanded
5169 let full = s:Generate(expanded)
5171 if full =~? '^fugitive:'
5172 let [pathdir, commit, path] = s:DirCommitFile(full)
5173 if commit =~# '^:\=\d$'
5177 let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':'))
5178 let branch = matchstr(expanded, '^[^:]*')
5182 let path = path[1:-1]
5183 elseif empty(s:Tree(dir))
5184 let path = '.git/' . full[strlen(dir)+1:-1]
5187 let path = full[strlen(s:Tree(dir))+1:-1]
5188 if path =~# '^\.git/'
5190 elseif isdirectory(full)
5196 if type ==# 'tree' && !empty(path)
5197 let path = s:sub(path, '/\=$', '/')
5199 if path =~# '^\.git/.*HEAD$' && filereadable(dir . '/' . path[5:-1])
5200 let body = readfile(dir . '/' . path[5:-1])[0]
5201 if body =~# '^\x\{40,\}$'
5205 elseif body =~# '^ref: refs/'
5206 let path = '.git/' . matchstr(body,'ref: \zs.*')
5211 if path =~# '^\.git/refs/remotes/.'
5213 let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+')
5214 let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5216 let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5217 let path = '.git/refs/heads/'.merge
5219 elseif path =~# '^\.git/refs/heads/.'
5220 let branch = path[16:-1]
5221 elseif !exists('branch')
5222 let branch = FugitiveHead()
5225 let r = fugitive#Config('branch.'.branch.'.remote')
5226 let m = fugitive#Config('branch.'.branch.'.merge')[11:-1]
5227 if r ==# '.' && !empty(m)
5228 let r2 = fugitive#Config('branch.'.m.'.remote')
5231 let m = fugitive#Config('branch.'.m.'.merge')[11:-1]
5237 if r ==# '.' || r ==# remote
5239 if path =~# '^\.git/refs/heads/.'
5240 let path = '.git/refs/heads/'.merge
5245 let line1 = a:count > 0 ? a:line1 : 0
5246 let line2 = a:count > 0 ? a:count : 0
5247 if empty(commit) && path !~# '^\.git/'
5248 if a:count < 0 && !empty(merge)
5253 let owner = s:Owner(@%)
5254 let [commit, exec_error] = s:ChompError(['merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? 'HEAD' : owner, '--'])
5258 if a:count > 0 && empty(a:args) && commit =~# '^\x\{40,\}$'
5259 let blame_list = tempname()
5260 call writefile([commit, ''], blame_list, 'b')
5261 let blame_in = tempname()
5262 silent exe '%write' blame_in
5263 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])
5265 let blame_regex = '^\^\x\+\s\+\zs\d\+\ze\s'
5266 if get(blame, 0) =~# blame_regex && get(blame, -1) =~# blame_regex
5267 let line1 = +matchstr(blame[0], blame_regex)
5268 let line2 = +matchstr(blame[-1], blame_regex)
5270 call s:throw("Can't browse to uncommitted change")
5277 let commit = readfile(fugitive#Find('.git/HEAD', dir), '', 1)[0]
5280 while commit =~# '^ref: ' && i < 10
5281 let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
5289 let raw = fugitive#RemoteUrl(remote)
5294 if raw =~# '^https\=://' && s:executable('curl')
5295 if !has_key(s:redirects, raw)
5296 let s:redirects[raw] = matchstr(system('curl -I ' .
5297 \ s:shellesc(raw . '/info/refs?service=git-upload-pack')),
5298 \ 'Location: \zs\S\+\ze/info/refs?')
5300 if len(s:redirects[raw])
5301 let raw = s:redirects[raw]
5307 \ 'repo': fugitive#repo(dir),
5309 \ 'revision': 'No longer provided',
5317 for Handler in get(g:, 'fugitive_browse_handlers', [])
5318 let url = call(Handler, [copy(opts)])
5325 call s:throw("No Gbrowse handler installed for '".raw."'")
5328 let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))')
5333 return 'echomsg '.string(url)
5334 elseif exists(':Browse') == 2
5335 return 'echomsg '.string(url).'|Browse '.url
5337 if !exists('g:loaded_netrw')
5338 runtime! autoload/netrw.vim
5340 if exists('*netrw#BrowseX')
5341 return 'echomsg '.string(url).'|call netrw#BrowseX('.string(url).', 0)'
5343 return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
5347 return 'echoerr ' . string(v:exception)
5351 " Section: Go to file
5353 nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
5354 function! fugitive#MapCfile(...) abort
5355 exe 'cnoremap <buffer> <expr> <Plug><cfile>' (a:0 ? a:1 : 'fugitive#Cfile()')
5356 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap <buffer> <Plug><cfile>"'
5357 if !exists('g:fugitive_no_maps')
5358 call s:Map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>', 1)
5359 call s:Map('n', '<C-W>f', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5360 call s:Map('n', '<C-W><C-F>', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5361 call s:Map('n', '<C-W>gf', '<SID>:tabfind <Plug><cfile><CR>', '<silent><unique>', 1)
5362 call s:Map('c', '<C-R><C-F>', '<Plug><cfile>', '<silent><unique>', 1)
5366 function! s:ContainingCommit() abort
5367 let commit = s:Owner(@%)
5368 return empty(commit) ? 'HEAD' : commit
5371 function! s:SquashArgument(...) abort
5372 if &filetype == 'fugitive'
5373 let commit = matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze ')
5374 elseif has_key(s:temp_files, s:cpath(expand('%:p')))
5375 let commit = matchstr(getline('.'), '\<\x\{4,\}\>')
5377 let commit = s:Owner(@%)
5379 return len(commit) && a:0 ? printf(a:1, commit) : commit
5382 function! s:RebaseArgument() abort
5383 return s:SquashArgument(' %s^')
5386 function! s:NavigateUp(count) abort
5387 let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
5391 let rev = matchstr(rev, '.*\ze/.\+', '')
5392 elseif rev =~# '.:.'
5393 let rev = matchstr(rev, '^.[^:]*:')
5406 function! s:MapMotion(lhs, rhs) abort
5407 call s:Map('n', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5408 call s:Map('o', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5409 call s:Map('x', a:lhs, ":<C-U>exe 'normal! gv'<Bar>" . a:rhs . "<CR>", "<silent>")
5412 function! fugitive#MapJumps(...) abort
5414 if get(b:, 'fugitive_type', '') ==# 'blob'
5415 let blame_map = 'Gblame<C-R>=v:count ? " --reverse" : ""<CR><CR>'
5416 call s:Map('n', '<2-LeftMouse>', ':<C-U>0,1' . blame_map, '<silent>')
5417 call s:Map('n', '<CR>', ':<C-U>0,1' . blame_map, '<silent>')
5418 call s:Map('n', 'o', ':<C-U>0,2' . blame_map, '<silent>')
5419 call s:Map('n', 'p', ':<C-U>0,3' . blame_map, '<silent>')
5420 call s:Map('n', 'gO', ':<C-U>0,4' . blame_map, '<silent>')
5421 call s:Map('n', 'O', ':<C-U>0,5' . blame_map, '<silent>')
5423 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>')
5424 call s:Map('n', 'dd', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<CR>", '<silent>')
5425 call s:Map('n', 'dh', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5426 call s:Map('n', 'ds', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5427 call s:Map('n', 'dv', ":<C-U>call <SID>DiffClose()<Bar>Gvdiffsplit!<CR>", '<silent>')
5428 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
5431 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5432 call s:Map('n', '<CR>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5433 call s:Map('n', 'o', ':<C-U>exe <SID>GF("split")<CR>', '<silent>')
5434 call s:Map('n', 'gO', ':<C-U>exe <SID>GF("vsplit")<CR>', '<silent>')
5435 call s:Map('n', 'O', ':<C-U>exe <SID>GF("tabedit")<CR>', '<silent>')
5436 call s:Map('n', 'p', ':<C-U>exe <SID>GF("pedit")<CR>', '<silent>')
5438 if !exists('g:fugitive_no_maps')
5439 if exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^<c-p>$'
5440 nnoremap <buffer> <silent> <C-P> :<C-U>execute line('.') == 1 ? 'CtrlP ' . fnameescape(<SID>Tree()) : <SID>PreviousItem(v:count1)<CR>
5442 nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>PreviousItem(v:count1)<CR>
5444 nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>NextItem(v:count1)<CR>
5446 call s:MapMotion('(', 'exe <SID>PreviousItem(v:count1)')
5447 call s:MapMotion(')', 'exe <SID>NextItem(v:count1)')
5448 call s:MapMotion('K', 'exe <SID>PreviousHunk(v:count1)')
5449 call s:MapMotion('J', 'exe <SID>NextHunk(v:count1)')
5450 call s:MapMotion('[c', 'exe <SID>PreviousHunk(v:count1)')
5451 call s:MapMotion(']c', 'exe <SID>NextHunk(v:count1)')
5452 call s:MapMotion('[/', 'exe <SID>PreviousFile(v:count1)')
5453 call s:MapMotion(']/', 'exe <SID>NextFile(v:count1)')
5454 call s:MapMotion('[m', 'exe <SID>PreviousFile(v:count1)')
5455 call s:MapMotion(']m', 'exe <SID>NextFile(v:count1)')
5456 call s:MapMotion('[[', 'exe <SID>PreviousSection(v:count1)')
5457 call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
5458 call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
5459 call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
5460 call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
5461 call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
5463 call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent>')
5464 call s:Map('n', 'dq', ":<C-U>call <SID>DiffClose()<CR>", '<silent>')
5465 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>')
5466 call s:Map('n', 'P', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5467 call s:Map('n', '~', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5468 call s:Map('n', 'C', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5469 call s:Map('n', 'cp', ":<C-U>echoerr 'Use gC'<CR>", '<silent>')
5470 call s:Map('n', 'gC', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5471 call s:Map('n', 'gc', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5472 call s:Map('n', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5473 call s:Map('x', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5475 nnoremap <buffer> c<Space> :Gcommit<Space>
5476 nnoremap <buffer> c<CR> :Gcommit<CR>
5477 nnoremap <buffer> cv<Space> :Gcommit -v<Space>
5478 nnoremap <buffer> cv<CR> :Gcommit -v<CR>
5479 nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
5480 nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
5481 nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
5482 nnoremap <buffer> <silent> cw :<C-U>Gcommit --amend --only<CR>
5483 nnoremap <buffer> <silent> cva :<C-U>Gcommit -v --amend<CR>
5484 nnoremap <buffer> <silent> cvc :<C-U>Gcommit -v<CR>
5485 nnoremap <buffer> <silent> cRa :<C-U>Gcommit --reset-author --amend<CR>
5486 nnoremap <buffer> <silent> cRe :<C-U>Gcommit --reset-author --amend --no-edit<CR>
5487 nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR>
5488 nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5489 nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5490 nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5491 nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5492 nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
5493 nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR>
5495 nnoremap <buffer> cr<Space> :Grevert<Space>
5496 nnoremap <buffer> cr<CR> :Grevert<CR>
5497 nnoremap <buffer> <silent> crc :<C-U>Grevert <C-R>=<SID>SquashArgument()<CR><CR>
5498 nnoremap <buffer> <silent> crn :<C-U>Grevert --no-commit <C-R>=<SID>SquashArgument()<CR><CR>
5499 nnoremap <buffer> <silent> cr? :help fugitive_cr<CR>
5501 nnoremap <buffer> cm<Space> :Gmerge<Space>
5502 nnoremap <buffer> cm<CR> :Gmerge<CR>
5503 nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
5505 nnoremap <buffer> cz<Space> :G stash<Space>
5506 nnoremap <buffer> cz<CR> :G stash<CR>
5507 nnoremap <buffer> <silent> cza :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5508 nnoremap <buffer> <silent> czA :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', 'stash@{' . v:count . '}'])<CR>
5509 nnoremap <buffer> <silent> czp :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5510 nnoremap <buffer> <silent> czP :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', 'stash@{' . v:count . '}'])<CR>
5511 nnoremap <buffer> <silent> czv :<C-U>exe 'Gedit' fugitive#RevParse('stash@{' . v:count . '}')<CR>
5512 nnoremap <buffer> <silent> czw :<C-U>exe <SID>EchoExec(['stash', '--keep-index'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5513 nnoremap <buffer> <silent> czz :<C-U>exe <SID>EchoExec(['stash'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5514 nnoremap <buffer> <silent> cz? :<C-U>help fugitive_cz<CR>
5516 nnoremap <buffer> co<Space> :G checkout<Space>
5517 nnoremap <buffer> co<CR> :G checkout<CR>
5518 nnoremap <buffer> coo :exe <SID>EchoExec(['checkout'] + split(<SID>SquashArgument()) + ['--'])<CR>
5519 nnoremap <buffer> co? :<C-U>help fugitive_co<CR>
5521 nnoremap <buffer> cb<Space> :G branch<Space>
5522 nnoremap <buffer> cb<CR> :G branch<CR>
5523 nnoremap <buffer> cb? :<C-U>help fugitive_cb<CR>
5525 nnoremap <buffer> r<Space> :Grebase<Space>
5526 nnoremap <buffer> r<CR> :Grebase<CR>
5527 nnoremap <buffer> <silent> ri :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><CR>
5528 nnoremap <buffer> <silent> rf :<C-U>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><CR>
5529 nnoremap <buffer> <silent> ru :<C-U>Grebase --interactive @{upstream}<CR>
5530 nnoremap <buffer> <silent> rp :<C-U>Grebase --interactive @{push}<CR>
5531 nnoremap <buffer> <silent> rw :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/reword/e<CR>
5532 nnoremap <buffer> <silent> rm :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/edit/e<CR>
5533 nnoremap <buffer> <silent> rd :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5534 nnoremap <buffer> <silent> rk :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5535 nnoremap <buffer> <silent> rx :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5536 nnoremap <buffer> <silent> rr :<C-U>Grebase --continue<CR>
5537 nnoremap <buffer> <silent> rs :<C-U>Grebase --skip<CR>
5538 nnoremap <buffer> <silent> re :<C-U>Grebase --edit-todo<CR>
5539 nnoremap <buffer> <silent> ra :<C-U>Grebase --abort<CR>
5540 nnoremap <buffer> <silent> r? :<C-U>help fugitive_r<CR>
5542 call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5543 call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5544 call s:Map('n', 'g?', ":<C-U>help fugitive-mappings<CR>", '<silent>')
5545 call s:Map('n', '<F1>', ":<C-U>help fugitive-mappings<CR>", '<silent>')
5549 function! s:StatusCfile(...) abort
5551 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5552 let info = s:StageInfo()
5553 let line = getline('.')
5554 if len(info.sigil) && len(info.section) && len(info.paths)
5555 if info.section ==# 'Unstaged' && info.sigil !=# '-'
5556 return [lead . info.relative[0], info.offset, 'normal!zv']
5557 elseif info.section ==# 'Staged' && info.sigil ==# '-'
5558 return ['@:' . info.relative[0], info.offset, 'normal!zv']
5560 return [':0:' . info.relative[0], info.offset, 'normal!zv']
5562 elseif len(info.paths)
5563 return [lead . info.relative[0]]
5564 elseif len(info.commit)
5565 return [info.commit]
5566 elseif line =~# '^\%(Head\|Merge\|Rebase\|Upstream\|Pull\|Push\): '
5567 return [matchstr(line, ' \zs.*')]
5573 function! fugitive#StatusCfile() abort
5574 let file = s:Generate(s:StatusCfile()[0])
5575 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5578 function! s:MessageCfile(...) abort
5580 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5581 if getline('.') =~# '^.\=\trenamed:.* -> '
5582 return lead . matchstr(getline('.'),' -> \zs.*')
5583 elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
5584 return lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
5585 elseif getline('.') =~# '^.\=\t.'
5586 return lead . matchstr(getline('.'),'\t\zs.*')
5587 elseif getline('.') =~# ': needs merge$'
5588 return lead . matchstr(getline('.'),'.*\ze: needs merge$')
5589 elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
5591 elseif getline('.') =~# '^\%(. \)\=On branch '
5592 return 'refs/heads/'.getline('.')[12:]
5593 elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
5594 return matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
5600 function! fugitive#MessageCfile() abort
5601 let file = s:Generate(s:MessageCfile())
5602 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5605 function! s:cfile() abort
5607 let myhash = s:DirRev(@%)[1]
5610 let myhash = fugitive#RevParse(myhash)
5615 if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w'
5616 let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
5619 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
5621 let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
5622 \ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
5624 if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40,\}\t'
5625 return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
5627 return [treebase . s:sub(getline('.'),'/$','')]
5634 if getline('.') =~# '^\d\{6\} \x\{40,\} \d\t'
5635 let ref = matchstr(getline('.'),'\x\{40,\}')
5636 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
5640 if getline('.') =~# '^ref: '
5641 let ref = strpart(getline('.'),5)
5643 elseif getline('.') =~# '^commit \x\{40,\}\>'
5644 let ref = matchstr(getline('.'),'\x\{40,\}')
5647 elseif getline('.') =~# '^parent \x\{40,\}\>'
5648 let ref = matchstr(getline('.'),'\x\{40,\}')
5649 let line = line('.')
5651 while getline(line) =~# '^parent '
5657 elseif getline('.') =~# '^tree \x\{40,\}$'
5658 let ref = matchstr(getline('.'),'\x\{40,\}')
5659 if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
5660 let ref = myhash.':'
5664 elseif getline('.') =~# '^object \x\{40,\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
5665 let ref = matchstr(getline('.'),'\x\{40,\}')
5666 let type = matchstr(getline(line('.')+1),'type \zs.*')
5668 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
5669 let ref = s:DirRev(@%)[1]
5671 elseif getline('.') =~# '^\l\{3,8\} \x\{40,\}\>'
5672 let ref = matchstr(getline('.'),'\x\{40,\}')
5673 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
5675 elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
5676 let ref = getline('.')[4:]
5678 elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
5679 let type = getline('.')[0]
5680 let lnum = line('.') - 1
5682 while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5683 if getline(lnum) =~# '^[ '.type.']'
5688 let offset += matchstr(getline(lnum), type.'\zs\d\+')
5689 let ref = getline(search('^'.type.'\{3\} [abciow12]/','bnW'))[4:-1]
5690 let dcmds = [offset, 'normal!zv']
5692 elseif getline('.') =~# '^rename from '
5693 let ref = 'a/'.getline('.')[12:]
5694 elseif getline('.') =~# '^rename to '
5695 let ref = 'b/'.getline('.')[10:]
5697 elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5698 let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
5699 let offset = matchstr(getline('.'), '+\zs\d\+')
5701 let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5702 let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5703 let dcmd = 'Gdiffsplit! +'.offset
5705 elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5706 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5707 let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5708 let dcmd = 'Gdiffsplit!'
5710 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5711 let line = getline(line('.')-1)
5712 let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5713 let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5714 let dcmd = 'Gdiffsplit!'
5716 elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
5717 let ref = getline('.')
5719 elseif expand('<cword>') =~# '^\x\{7,\}\>'
5720 return [expand('<cword>')]
5735 let prefixes.a = myhash.'^:'
5736 let prefixes.b = myhash.':'
5738 let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5740 let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5743 if ref ==# '/dev/null'
5745 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
5749 return [ref, dcmd . ' ' . s:fnameescape(dref)] + dcmds
5751 return [ref] + dcmds
5759 function! s:GF(mode) abort
5761 let results = &filetype ==# 'fugitive' ? s:StatusCfile() : &filetype ==# 'gitcommit' ? [s:MessageCfile()] : s:cfile()
5763 return 'echoerr ' . string(v:exception)
5766 return 'G' . a:mode .
5767 \ ' +' . escape(results[1], ' ') . ' ' .
5768 \ s:fnameescape(results[0]) . join(map(results[2:-1], '"|" . v:val'), '')
5769 elseif len(results) && len(results[0])
5770 return 'G' . a:mode . ' ' . s:fnameescape(results[0])
5776 function! fugitive#Cfile() abort
5778 let results = s:cfile()
5780 let cfile = expand('<cfile>')
5781 if &includeexpr =~# '\<v:fname\>'
5782 sandbox let cfile = eval(substitute(&includeexpr, '\C\<v:fname\>', '\=string(cfile)', 'g'))
5785 elseif len(results) > 1
5786 let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' '
5788 return pre . s:fnameescape(s:Generate(results[0]))
5791 " Section: Statusline
5793 function! fugitive#Statusline(...) abort
5798 let commit = s:DirCommitFile(@%)[1]
5800 let status .= ':' . commit[0:6]
5802 let status .= '('.FugitiveHead(7).')'
5803 return '[Git'.status.']'
5806 function! fugitive#statusline(...) abort
5807 return fugitive#Statusline()
5810 function! fugitive#head(...) abort
5815 return fugitive#Head(a:0 ? a:1 : 0)
5820 function! fugitive#Foldtext() abort
5821 if &foldmethod !=# 'syntax'
5825 let line_foldstart = getline(v:foldstart)
5826 if line_foldstart =~# '^diff '
5827 let [add, remove] = [-1, -1]
5829 for lnum in range(v:foldstart, v:foldend)
5830 let line = getline(lnum)
5831 if filename ==# '' && line =~# '^[+-]\{3\} [abciow12]/'
5832 let filename = line[6:-1]
5836 elseif line =~# '^-'
5838 elseif line =~# '^Binary '
5843 let filename = matchstr(line_foldstart, '^diff .\{-\} [abciow12]/\zs.*\ze [abciow12]/')
5846 let filename = line_foldstart[5:-1]
5849 return 'Binary: '.filename
5851 return (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
5853 elseif line_foldstart =~# '^# .*:$'
5854 let lines = getline(v:foldstart, v:foldend)
5855 call filter(lines, 'v:val =~# "^#\t"')
5856 cal map(lines, "s:sub(v:val, '^#\t%(modified: +|renamed: +)=', '')")
5857 cal map(lines, "s:sub(v:val, '^([[:alpha:] ]+): +(.*)', '\\2 (\\1)')")
5858 return line_foldstart.' '.join(lines, ', ')
5863 function! fugitive#foldtext() abort
5864 return fugitive#Foldtext()
5867 augroup fugitive_folding
5869 autocmd User Fugitive
5870 \ if &filetype =~# '^git\%(commit\)\=$' && &foldtext ==# 'foldtext()' |
5871 \ set foldtext=fugitive#Foldtext() |
5875 " Section: Initialization
5877 function! fugitive#Init() abort
5878 if exists('#User#FugitiveBoot')
5880 let [save_mls, &modelines] = [&mls, 0]
5881 doautocmd User FugitiveBoot
5886 if !exists('g:fugitive_no_maps')
5887 call s:Map('c', '<C-R><C-G>', '<SID>fnameescape(fugitive#Object(@%))', '<expr>')
5888 call s:Map('n', 'y<C-G>', ':<C-U>call setreg(v:register, fugitive#Object(@%))<CR>', '<silent>')
5890 if expand('%:p') =~# ':[\/][\/]'
5891 let &l:path = s:sub(&path, '^\.%(,|$)', '')
5894 if stridx(&tags, escape(dir, ', ')) == -1
5895 let actualdir = fugitive#Find('.git/', dir)
5896 if filereadable(actualdir . 'tags')
5897 let &l:tags = escape(actualdir . 'tags', ', ').','.&tags
5899 if &filetype !=# '' && filereadable(actualdir . &filetype . '.tags')
5900 let &l:tags = escape(actualdir . &filetype . '.tags', ', ').','.&tags
5904 let [save_mls, &modelines] = [&mls, 0]
5905 call s:define_commands()
5906 doautocmd User Fugitive
5912 function! fugitive#is_git_dir(path) abort
5913 return FugitiveIsGitDir(a:path)
5916 function! fugitive#extract_git_dir(path) abort
5917 return FugitiveExtractGitDir(a:path)
5920 function! fugitive#detect(path) abort
5921 return FugitiveDetect(a:path)