1 " Location: autoload/fugitive.vim
2 " Maintainer: Tim Pope <http://tpo.pe/>
4 if exists('g:autoloaded_fugitive')
7 let g:autoloaded_fugitive = 1
9 if !exists('g:fugitive_git_executable')
10 let g:fugitive_git_executable = 'git'
11 elseif g:fugitive_git_executable =~# '^\w\+='
12 let g:fugitive_git_executable = 'env ' . g:fugitive_git_executable
17 function! s:function(name) abort
18 return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
21 function! s:sub(str,pat,rep) abort
22 return substitute(a:str,'\v\C'.a:pat,a:rep,'')
25 function! s:gsub(str,pat,rep) abort
26 return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
29 function! s:Uniq(list) abort
33 let str = string(a:list[i])
35 call remove(a:list, i)
44 function! s:winshell() abort
45 return has('win32') && &shellcmdflag !~# '^-'
48 function! s:shellesc(arg) abort
49 if type(a:arg) == type([])
50 return join(map(copy(a:arg), 's:shellesc(v:val)'))
51 elseif a:arg =~ '^[A-Za-z0-9_/:.-]\+$'
54 return '"'.s:gsub(s:gsub(a:arg, '"', '""'), '\%', '"%"').'"'
56 return shellescape(a:arg)
60 let s:fnameescape = " \t\n*?[{`$\\%#'\"|!<"
61 function! s:fnameescape(file) abort
62 if type(a:file) == type([])
63 return join(map(copy(a:file), 's:fnameescape(v:val)'))
64 elseif exists('*fnameescape')
65 return fnameescape(a:file)
67 return escape(a:file, s:fnameescape)
71 function! s:throw(string) abort
72 throw 'fugitive: '.a:string
75 function! s:DirCheck(...) abort
76 if !empty(a:0 ? s:Dir(a:1) : s:Dir())
78 elseif empty(bufname(''))
79 return 'return ' . string('echoerr "fugitive: blank buffer unsupported (edit a file from a repository)"')
81 return 'return ' . string('echoerr "fugitive: file does not belong to a Git repository"')
85 function! s:Mods(mods, ...) abort
86 let mods = substitute(a:mods, '\C<mods>', '', '')
87 let mods = mods =~# '\S$' ? mods . ' ' : mods
88 if a:0 && mods !~# '\<\%(aboveleft\|belowright\|leftabove\|rightbelow\|topleft\|botright\|tab\)\>'
89 let mods = a:1 . ' ' . mods
91 return substitute(mods, '\s\+', ' ', 'g')
94 function! s:Slash(path) abort
95 if exists('+shellslash')
96 return tr(a:path, '\', '/')
102 function! s:Resolve(path) abort
103 let path = resolve(a:path)
105 let path = FugitiveVimPath(fnamemodify(fnamemodify(path, ':h'), ':p') . fnamemodify(path, ':t'))
110 function! s:cpath(path, ...) abort
111 if exists('+fileignorecase') && &fileignorecase
112 let path = FugitiveVimPath(tolower(a:path))
114 let path = FugitiveVimPath(a:path)
116 return a:0 ? path ==# s:cpath(a:1) : path
119 function! s:Cd(...) abort
120 let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'
128 exe cd s:fnameescape(a:1)
129 return cd . ' ' . s:fnameescape(cwd)
132 let s:executables = {}
134 function! s:executable(binary) abort
135 if !has_key(s:executables, a:binary)
136 let s:executables[a:binary] = executable(a:binary)
138 return s:executables[a:binary]
141 let s:nowait = v:version >= 704 ? '<nowait>' : ''
143 function! s:Map(mode, lhs, rhs, ...) abort
144 for mode in split(a:mode, '\zs')
145 let flags = (a:0 ? a:1 : '') . (a:rhs =~# '<Plug>' ? '' : '<script>')
148 let keys = get(g:, mode.'remap', {})
149 if type(keys) == type([])
153 if has_key(keys, head)
154 let head = keys[head]
160 let tail = matchstr(head, '<[^<>]*>$\|.$') . tail
161 let head = substitute(head, '<[^<>]*>$\|.$', '', '')
163 if flags !~# '<unique>' || empty(mapcheck(head.tail, mode))
164 exe mode.'map <buffer>' s:nowait flags head.tail a:rhs
166 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') .
167 \ '|sil! exe "' . mode . 'unmap <buffer> ' . head.tail . '"'
175 function! s:QuickfixGet(nr, ...) abort
177 return call('getqflist', a:000)
179 return call('getloclist', [a:nr] + a:000)
183 function! s:QuickfixSet(nr, ...) abort
185 return call('setqflist', a:000)
187 return call('setloclist', [a:nr] + a:000)
191 function! s:QuickfixCreate(nr, opts) abort
192 if has('patch-7.4.2200')
193 call s:QuickfixSet(a:nr, [], ' ', a:opts)
195 call s:QuickfixSet(a:nr, [], ' ')
199 function! s:QuickfixStream(nr, title, cmd, first, callback, ...) abort
200 call s:QuickfixCreate(a:nr, {'title': a:title})
202 exe a:nr < 0 ? 'copen' : 'lopen'
208 let lines = split(s:SystemError(s:shellesc(a:cmd))[0], "\n")
210 call extend(buffer, call(a:callback, a:000 + [line]))
212 call s:QuickfixSet(a:nr, remove(buffer, 0, -1), 'a')
216 call s:QuickfixSet(a:nr, extend(buffer, call(a:callback, a:000 + [0])), 'a')
218 if a:first && len(s:QuickfixGet(a:nr))
220 return a:nr < 0 ? 'cfirst' : 'lfirst'
228 function! s:UserCommandList(...) abort
229 let git = split(get(g:, 'fugitive_git_command', g:fugitive_git_executable), '\s\+')
230 let dir = a:0 ? s:Dir(a:1) : ''
232 let tree = s:Tree(dir)
234 call add(git, '--git-dir=' . FugitiveGitPath(dir))
235 elseif len(tree) && s:cpath(tree) !=# s:cpath(getcwd())
236 if fugitive#GitVersion(1, 8, 5)
237 call extend(git, ['-C', FugitiveGitPath(tree)])
239 throw 'fugitive: Git 1.8.5 or higher required to change directory'
246 function! s:UserCommand(...) abort
247 return s:shellesc(call('s:UserCommandList', a:0 ? [a:1] : []) + (a:0 ? a:2 : []))
250 let s:git_versions = {}
251 function! fugitive#GitVersion(...) abort
252 if !has_key(s:git_versions, g:fugitive_git_executable)
253 let s:git_versions[g:fugitive_git_executable] = matchstr(system(g:fugitive_git_executable.' --version'), '\d[^[:space:]]\+')
256 return s:git_versions[g:fugitive_git_executable]
258 let components = split(s:git_versions[g:fugitive_git_executable], '\D\+')
262 for i in range(len(a:000))
263 if a:000[i] > +get(components, i)
265 elseif a:000[i] < +get(components, i)
269 return a:000[i] ==# get(components, i)
272 let s:commondirs = {}
273 function! fugitive#CommonDir(dir) abort
277 if !has_key(s:commondirs, a:dir)
278 if getfsize(a:dir . '/HEAD') < 10
279 let s:commondirs[a:dir] = ''
280 elseif filereadable(a:dir . '/commondir')
281 let cdir = get(readfile(a:dir . '/commondir', 1), 0, '')
282 if cdir =~# '^/\|^\a:/'
283 let s:commondirs[a:dir] = s:Slash(FugitiveVimPath(cdir))
285 let s:commondirs[a:dir] = simplify(a:dir . '/' . cdir)
288 let s:commondirs[a:dir] = a:dir
291 return s:commondirs[a:dir]
294 function! s:Dir(...) abort
295 return a:0 ? FugitiveGitDir(a:1) : FugitiveGitDir()
298 function! s:Tree(...) abort
299 return a:0 ? FugitiveWorkTree(a:1) : FugitiveWorkTree()
302 function! s:HasOpt(args, ...) abort
303 let args = a:args[0 : index(a:args, '--')]
304 let opts = copy(a:000)
305 if type(opts[0]) == type([])
306 if empty(args) || index(opts[0], args[0]) == -1
312 if index(args, opt) != -1
318 function! s:PreparePathArgs(cmd, dir, literal) abort
319 let literal_supported = fugitive#GitVersion(1, 9)
320 if a:literal && literal_supported
321 call insert(a:cmd, '--literal-pathspecs')
323 let split = index(a:cmd, '--')
324 for i in range(split < 0 ? len(a:cmd) : split)
325 if type(a:cmd[i]) == type(0)
326 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
332 for i in range(split + 1, len(a:cmd) - 1)
333 if type(a:cmd[i]) == type(0)
334 let a:cmd[i] = fugitive#Path(bufname(a:cmd[i]), './', a:dir)
336 let a:cmd[i] = fugitive#Path(a:cmd[i], './', a:dir)
337 elseif !literal_supported
338 let a:cmd[i] = substitute(a:cmd[i], '^:\%(/\|([^)]*)\)\=:\=', './', '')
344 let s:prepare_env = {
345 \ 'sequence.editor': 'GIT_SEQUENCE_EDITOR',
346 \ 'core.editor': 'GIT_EDITOR',
347 \ 'core.askpass': 'GIT_ASKPASS',
349 function! fugitive#PrepareDirEnvArgv(...) abort
350 if a:0 && type(a:1) ==# type([])
351 let cmd = a:000[1:-1] + a:1
353 let cmd = copy(a:000)
358 if cmd[i] =~# '^$\|[\/.]' && cmd[i] !~# '^-'
359 let dir = remove(cmd, 0)
360 elseif cmd[i] =~# '^--git-dir='
361 let dir = remove(cmd, 0)[10:-1]
362 elseif type(cmd[i]) ==# type(0)
363 let dir = s:Dir(remove(cmd, i))
364 elseif cmd[i] ==# '-c' && len(cmd) > i + 1
365 let key = matchstr(cmd[i+1], '^[^=]*')
366 if has_key(s:prepare_env, tolower(key)) || key !~# '\.'
367 let var = get(s:prepare_env, tolower(key), key)
368 let val = matchstr(cmd[i+1], '=\zs.*')
371 if fugitive#GitVersion(1, 8) && cmd[i+1] =~# '\.'
374 call remove(cmd, i, i + 1)
376 elseif cmd[i] =~# '^--.*pathspecs$'
377 let explicit_pathspec_option = 1
378 if fugitive#GitVersion(1, 9)
383 elseif cmd[i] !~# '^-'
392 call s:PreparePathArgs(cmd, dir, !exists('explicit_pathspec_option'))
393 return [dir, env, cmd]
396 function! s:BuildShell(dir, env, args) abort
397 let cmd = copy(a:args)
398 let tree = s:Tree(a:dir)
400 for [var, val] in items(a:env)
402 let pre .= 'set ' . var . '=' . s:shellesc(val) . '& '
404 let pre = (len(pre) ? pre : 'env ') . var . '=' . s:shellesc(val) . ' '
407 if empty(tree) || index(cmd, '--') == len(cmd) - 1
408 call insert(cmd, '--git-dir=' . FugitiveGitPath(a:dir))
409 elseif fugitive#GitVersion(1, 8, 5)
410 call extend(cmd, ['-C', FugitiveGitPath(tree)], 'keep')
412 let pre = 'cd ' . s:shellesc(tree) . (s:winshell() ? '& ' : '; ') . pre
414 return pre . g:fugitive_git_executable . ' ' . join(map(cmd, 's:shellesc(v:val)'))
417 function! fugitive#Prepare(...) abort
418 let [dir, env, argv] = call('fugitive#PrepareDirEnvArgv', a:000)
419 return s:BuildShell(dir, env, argv)
422 function! s:SystemError(cmd, ...) abort
424 if &shellredir ==# '>' && &shell =~# 'sh\|cmd'
425 let shellredir = &shellredir
429 set shellredir=>%s\ 2>&1
432 let out = call('system', [type(a:cmd) ==# type([]) ? fugitive#Prepare(a:cmd) : a:cmd] + a:000)
433 return [out, v:shell_error]
434 catch /^Vim\%((\a\+)\)\=:E484:/
435 let opts = ['shell', 'shellcmdflag', 'shellredir', 'shellquote', 'shellxquote', 'shellxescape', 'shellslash']
436 call filter(opts, 'exists("+".v:val) && !empty(eval("&".v:val))')
437 call map(opts, 'v:val."=".eval("&".v:val)')
438 call s:throw('failed to run `' . a:cmd . '` with ' . join(opts, ' '))
440 if exists('shellredir')
441 let &shellredir = shellredir
446 function! s:ChompError(...) abort
447 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
448 return [s:sub(out, '\n$', ''), exec_error]
451 function! s:ChompDefault(default, ...) abort
452 let [out, exec_error] = call('s:ChompError', a:000)
453 return exec_error ? a:default : out
456 function! s:LinesError(...) abort
457 let [out, exec_error] = call('s:ChompError', a:000)
458 return [len(out) && !exec_error ? split(out, "\n", 1) : [], exec_error]
461 function! s:NullError(...) abort
462 let [out, exec_error] = s:SystemError(call('fugitive#Prepare', a:000))
463 return [exec_error ? [] : split(out, "\1"), exec_error ? substitute(out, "\n$", "", "") : '', exec_error]
466 function! s:TreeChomp(...) abort
467 let cmd = call('fugitive#Prepare', a:000)
468 let [out, exec_error] = s:SystemError(cmd)
469 let out = s:sub(out, '\n$', '')
473 throw 'fugitive: error running `' . cmd . '`: ' . out
476 function! s:EchoExec(...) abort
477 echo call('s:ChompError', a:000)[0]
478 call fugitive#ReloadStatus(-1, 1)
482 function! fugitive#Head(...) abort
483 let dir = a:0 > 1 ? a:2 : s:Dir()
484 if empty(dir) || !filereadable(fugitive#Find('.git/HEAD', dir))
487 let head = readfile(fugitive#Find('.git/HEAD', dir))[0]
489 return substitute(head, '\C^ref: \%(refs/\%(heads/\|remotes/\|tags/\)\=\)\=', '', '')
490 elseif head =~# '^\x\{40,\}$'
491 let len = a:0 ? a:1 : 0
492 return len < 0 ? head : len ? head[0:len-1] : ''
498 function! fugitive#RevParse(rev, ...) abort
499 let [hash, exec_error] = s:ChompError([a:0 ? a:1 : s:Dir(), 'rev-parse', '--verify', a:rev, '--'])
500 if !exec_error && hash =~# '^\x\{40,\}$'
503 throw 'fugitive: rev-parse '.a:rev.': '.hash
506 function! s:ConfigTimestamps(dir, dict) abort
507 let files = ['/etc/gitconfig', '~/.gitconfig',
508 \ len($XDG_CONFIG_HOME) ? $XDG_CONFIG_HOME . '/git/config' : '~/.config/git/config']
510 call add(files, fugitive#Find('.git/config', a:dir))
512 call extend(files, get(a:dict, 'include.path', []))
513 return join(map(files, 'getftime(expand(v:val))'), ',')
517 function! fugitive#Config(...) abort
520 if a:0 >= 2 && type(a:2) == type({})
521 let name = substitute(a:1, '^[^.]\+\|[^.]\+$', '\L&', 'g')
522 return len(a:1) ? get(get(a:2, name, []), 0, '') : a:2
526 elseif a:0 == 1 && type(a:1) == type({})
528 elseif a:0 == 1 && a:1 =~# '^[[:alnum:]-]\+\.'
533 let name = substitute(name, '^[^.]\+\|[^.]\+$', '\L&', 'g')
534 let key = len(dir) ? dir : '_'
535 if has_key(s:config, key) && s:config[key][0] ==# s:ConfigTimestamps(dir, s:config[key][1])
536 let dict = s:config[key][1]
539 let [lines, message, exec_error] = s:NullError([dir, 'config', '--list', '-z'])
544 let key = matchstr(line, "^[^\n]*")
545 if !has_key(dict, key)
548 call add(dict[key], strpart(line, len(key) + 1))
550 let s:config[dir] = [s:ConfigTimestamps(dir, dict), dict]
553 return len(name) ? get(get(dict, name, []), 0, '') : dict
556 function! s:Remote(dir) abort
557 let head = FugitiveHead(0, a:dir)
558 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
560 while remote ==# '.' && i > 0
561 let head = matchstr(fugitive#Config('branch.' . head . '.merge'), 'refs/heads/\zs.*')
562 let remote = len(head) ? fugitive#Config('branch.' . head . '.remote') : ''
565 return remote =~# '^\.\=$' ? 'origin' : remote
568 function! fugitive#RemoteUrl(...) abort
569 let dir = a:0 > 1 ? a:2 : s:Dir()
570 let remote = !a:0 || a:1 =~# '^\.\=$' ? s:Remote(dir) : a:1
571 if !fugitive#GitVersion(2, 7)
572 return fugitive#Config('remote.' . remote . '.url')
574 return s:ChompDefault('', [dir, 'remote', 'get-url', remote, '--'])
577 " Section: Repository Object
579 function! s:add_methods(namespace, method_names) abort
580 for name in a:method_names
581 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
585 function! s:Command(command, line1, line2, range, bang, mods, arg, args) abort
587 if a:command =~# '^\l[[:alnum:]-]\+$'
588 return fugitive#Command(a:line1, a:line2, a:range, a:bang, s:Mods(a:mods), a:command . ' ' . a:arg)
590 return s:{a:command}Command(a:line1, a:line2, a:range, a:line2, a:bang, s:Mods(a:mods), '', a:arg, a:args)
592 return 'echoerr ' . string(v:exception)
597 function! s:command(definition, ...) abort
598 let def = a:definition
599 if !has('patch-7.4.542')
600 let def = substitute(def, '-addr=\S\+ ', '', '')
602 if !has('patch-8.1.560')
603 let def = substitute(def, '-addr=other ', '', '')
606 call add(s:commands, def . ' execute s:Command(' . string(a:1) . ", <line1>, <count>, +'<range>', <bang>0, '<mods>', <q-args>, [<f-args>])")
608 call add(s:commands, def)
612 function! s:define_commands() abort
613 for command in s:commands
614 exe 'command! -buffer '.command
618 let s:repo_prototype = {}
621 function! fugitive#repo(...) abort
622 let dir = a:0 ? s:Dir(a:1) : (len(s:Dir()) ? s:Dir() : FugitiveExtractGitDir(expand('%:p')))
624 if has_key(s:repos, dir)
625 let repo = get(s:repos, dir)
627 let repo = {'git_dir': dir}
628 let s:repos[dir] = repo
630 return extend(repo, s:repo_prototype, 'keep')
632 call s:throw('not a Git repository')
635 function! s:repo_dir(...) dict abort
636 return join([self.git_dir]+a:000,'/')
639 function! s:repo_tree(...) dict abort
640 let dir = s:Tree(self.git_dir)
642 call s:throw('no work tree')
644 return join([dir]+a:000,'/')
648 function! s:repo_bare() dict abort
649 if self.dir() =~# '/\.git$'
652 return s:Tree(self.git_dir) ==# ''
656 function! s:repo_find(object) dict abort
657 return fugitive#Find(a:object, self.git_dir)
660 function! s:repo_translate(rev) dict abort
661 return s:Slash(fugitive#Find(substitute(a:rev, '^/', ':(top)', ''), self.git_dir))
664 function! s:repo_head(...) dict abort
665 return fugitive#Head(a:0 ? a:1 : 0, self.git_dir)
668 call s:add_methods('repo',['dir','tree','bare','find','translate','head'])
670 function! s:repo_prepare(...) dict abort
671 return call('fugitive#Prepare', [self.git_dir] + a:000)
674 function! s:repo_git_command(...) dict abort
675 let git = s:UserCommand() . ' --git-dir='.s:shellesc(self.git_dir)
676 return git.join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
679 function! s:repo_git_chomp(...) dict abort
680 let git = g:fugitive_git_executable . ' --git-dir='.s:shellesc(self.git_dir)
681 let output = git . join(map(copy(a:000),'" ".s:shellesc(v:val)'),'')
682 return s:sub(system(output), '\n$', '')
685 function! s:repo_git_chomp_in_tree(...) dict abort
686 let cdback = s:Cd(self.tree())
688 return call(self.git_chomp, a:000, self)
694 function! s:repo_rev_parse(rev) dict abort
695 return fugitive#RevParse(a:rev, self.git_dir)
698 call s:add_methods('repo',['prepare','git_command','git_chomp','git_chomp_in_tree','rev_parse'])
700 function! s:repo_superglob(base) dict abort
701 return map(fugitive#CompleteObject(a:base, self.git_dir), 'substitute(v:val, ''\\\(.\)'', ''\1'', "g")')
704 call s:add_methods('repo',['superglob'])
706 function! s:repo_config(name) dict abort
707 return fugitive#Config(a:name, self.git_dir)
710 function! s:repo_user() dict abort
711 let username = self.config('user.name')
712 let useremail = self.config('user.email')
713 return username.' <'.useremail.'>'
716 call s:add_methods('repo',['config', 'user'])
720 function! s:DirCommitFile(path) abort
721 let vals = matchlist(s:Slash(a:path), '\c^fugitive:\%(//\)\=\(.\{-\}\)\%(//\|::\)\(\x\{40,\}\|[0-3]\)\(/.*\)\=$')
728 function! s:DirRev(url) abort
729 let [dir, commit, file] = s:DirCommitFile(a:url)
730 return [dir, (commit =~# '^.$' ? ':' : '') . commit . substitute(file, '^/', ':', '')]
733 function! s:Owner(path, ...) abort
734 let dir = a:0 ? a:1 : s:Dir()
738 let actualdir = fugitive#Find('.git/', dir)
739 let [pdir, commit, file] = s:DirCommitFile(a:path)
740 if s:cpath(dir, pdir)
741 if commit =~# '^\x\{40,\}$'
743 elseif commit ==# '2'
746 if filereadable(actualdir . 'MERGE_HEAD')
747 let merge_head = 'MERGE_HEAD'
748 elseif filereadable(actualdir . 'REBASE_HEAD')
749 let merge_head = 'REBASE_HEAD'
754 return merge_head . '^{}'
755 elseif commit ==# '1'
756 return s:TreeChomp('merge-base', 'HEAD', merge_head, '--')
759 let path = fnamemodify(a:path, ':p')
760 if s:cpath(actualdir, strpart(path, 0, len(actualdir))) && a:path =~# 'HEAD$'
761 return strpart(path, len(actualdir))
763 let refs = fugitive#Find('.git/refs', dir)
764 if s:cpath(refs . '/', path[0 : len(refs)]) && path !~# '[\/]$'
765 return strpart(path, len(refs) - 4)
770 function! fugitive#Real(url) abort
774 let [dir, commit, file] = s:DirCommitFile(a:url)
776 let tree = s:Tree(dir)
777 return FugitiveVimPath((len(tree) ? tree : dir) . file)
779 let pre = substitute(matchstr(a:url, '^\a\a\+\ze:'), '^.', '\u&', '')
780 if len(pre) && pre !=? 'fugitive' && exists('*' . pre . 'Real')
781 let url = {pre}Real(a:url)
783 let url = fnamemodify(a:url, ':p' . (a:url =~# '[\/]$' ? '' : ':s?[\/]$??'))
785 return FugitiveVimPath(empty(url) ? a:url : url)
788 function! fugitive#Path(url, ...) abort
792 let dir = a:0 > 1 ? a:2 : s:Dir()
793 let tree = s:Tree(dir)
795 return fugitive#Real(a:url)
797 let path = s:Slash(fugitive#Real(a:url))
800 while s:cpath(tree . '/', (cwd . '/')[0 : len(tree)])
801 if s:cpath(cwd . '/', path[0 : len(cwd)])
802 if strpart(path, len(cwd) + 1) =~# '^\.git\%(/\|$\)'
805 return a:1[0:-2] . (empty(lead) ? './' : lead) . strpart(path, len(cwd) + 1)
807 let cwd = fnamemodify(cwd, ':h')
810 return a:1[0:-2] . path
813 let temp_state = s:TempState(url)
814 if has_key(temp_state, 'bufnr')
815 let url = bufname(temp_state.bufnr)
817 let url = s:Slash(fnamemodify(url, ':p'))
818 if url =~# '/$' && s:Slash(a:url) !~# '/$'
821 let [argdir, commit, file] = s:DirCommitFile(a:url)
822 if len(argdir) && s:cpath(argdir) !=# s:cpath(dir)
824 elseif len(dir) && s:cpath(url[0 : len(dir)]) ==# s:cpath(dir . '/')
825 let file = '/.git'.url[strlen(dir) : -1]
826 elseif len(tree) && s:cpath(url[0 : len(tree)]) ==# s:cpath(tree . '/')
827 let file = url[len(tree) : -1]
828 elseif s:cpath(url) ==# s:cpath(tree)
831 if empty(file) && a:1 =~# '^$\|^[.:]/$'
832 return FugitiveGitPath(fugitive#Real(a:url))
834 return substitute(file, '^/', a:1, '')
837 function! s:Relative(...) abort
838 return fugitive#Path(@%, a:0 ? a:1 : ':(top)', a:0 > 1 ? a:2 : s:Dir())
841 function! fugitive#Find(object, ...) abort
842 if type(a:object) == type(0)
843 let name = bufname(a:object)
844 return FugitiveVimPath(name =~# '^$\|^/\|^\a\+:' ? name : getcwd() . '/' . name)
845 elseif a:object =~# '^[~$]'
846 let prefix = matchstr(a:object, '^[~$]\i*')
847 let owner = expand(prefix)
848 return FugitiveVimPath((len(owner) ? owner : prefix) . strpart(a:object, len(prefix)))
849 elseif s:Slash(a:object) =~# '^$\|^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
850 return FugitiveVimPath(a:object)
851 elseif s:Slash(a:object) =~# '^\.\.\=\%(/\|$\)'
852 return FugitiveVimPath(simplify(getcwd() . '/' . a:object))
854 let dir = a:0 ? a:1 : s:Dir()
856 let file = matchstr(a:object, '^\%(:\d:\|[^:]*:\)\zs.*', '', '')
857 let dir = FugitiveExtractGitDir(file)
859 return fnamemodify(FugitiveVimPath(len(file) ? file : a:object), ':p')
862 let rev = s:Slash(a:object)
863 let tree = s:Tree(dir)
864 let base = len(tree) ? tree : 'fugitive://' . dir . '//0'
866 let f = len(tree) ? tree . '/.git' : dir
867 elseif rev =~# '^\.git/'
868 let f = substitute(rev, '^\.git', '', '')
869 let cdir = fugitive#CommonDir(dir)
870 if f =~# '^/\.\./\.\.\%(/\|$\)'
871 let f = simplify(len(tree) ? tree . f[3:-1] : dir . f)
872 elseif f =~# '^/\.\.\%(/\|$\)'
873 let f = base . f[3:-1]
874 elseif cdir !=# dir && (
875 \ f =~# '^/\%(config\|hooks\|info\|logs/refs\|objects\|refs\|worktrees\)\%(/\|$\)' ||
876 \ f !~# '^/\%(index$\|index\.lock$\|\w*MSG$\|\w*HEAD$\|logs/\w*HEAD$\|logs$\|rebase-\w\+\)\%(/\|$\)' &&
877 \ getftime(FugitiveVimPath(dir . f)) < 0 && getftime(FugitiveVimPath(cdir . f)) >= 0)
878 let f = simplify(cdir . f)
880 let f = simplify(dir . f)
884 elseif rev =~# '^\.\%(/\|$\)'
885 let f = base . rev[1:-1]
886 elseif rev =~# '^::\%(/\|\a\+\:\)'
888 elseif rev =~# '^::\.\.\=\%(/\|$\)'
889 let f = simplify(getcwd() . '/' . rev[2:-1])
891 let f = base . '/' . rev[2:-1]
892 elseif rev =~# '^:\%([0-3]:\)\=\.\.\=\%(/\|$\)\|^:[0-3]:\%(/\|\a\+:\)'
893 let f = rev =~# '^:\%([0-3]:\)\=\.' ? simplify(getcwd() . '/' . matchstr(rev, '\..*')) : rev[3:-1]
894 if s:cpath(base . '/', (f . '/')[0 : len(base)])
895 let f = 'fugitive://' . dir . '//' . +matchstr(rev, '^:\zs\d\ze:') . '/' . strpart(f, len(base) + 1)
897 let altdir = FugitiveExtractGitDir(f)
898 if len(altdir) && !s:cpath(dir, altdir)
899 return fugitive#Find(a:object, altdir)
902 elseif rev =~# '^:[0-3]:'
903 let f = 'fugitive://' . dir . '//' . rev[1] . '/' . rev[3:-1]
905 if $GIT_INDEX_FILE =~# '/[^/]*index[^/]*\.lock$' && s:cpath(fnamemodify($GIT_INDEX_FILE,':p')[0:strlen(dir)]) ==# s:cpath(dir . '/') && filereadable($GIT_INDEX_FILE)
906 let f = fnamemodify($GIT_INDEX_FILE, ':p')
908 let f = fugitive#Find('.git/index', dir)
910 elseif rev =~# '^:(\%(top\|top,literal\|literal,top\|literal\))'
911 let f = matchstr(rev, ')\zs.*')
912 if f=~# '^\.\.\=\%(/\|$\)'
913 let f = simplify(getcwd() . '/' . f)
914 elseif f !~# '^/\|^\%(\a\a\+:\).*\%(//\|::\)' . (has('win32') ? '\|^\a:/' : '')
915 let f = base . '/' . f
917 elseif rev =~# '^:/\@!'
918 let f = 'fugitive://' . dir . '//0/' . rev[1:-1]
921 let commit = substitute(matchstr(rev, '^[^:.-][^:]*\|^:.*'), '^@\%($\|[~^]\|@{\)\@=', 'HEAD', '')
922 let file = substitute(matchstr(rev, '^[^:.-][^:]*\zs:.*'), '^:', '/', '')
923 if file =~# '^/\.\.\=\%(/\|$\)\|^//\|^/\a\+:'
924 let file = file =~# '^/\.' ? simplify(getcwd() . file) : file[1:-1]
925 if s:cpath(base . '/', (file . '/')[0 : len(base)])
926 let file = '/' . strpart(file, len(base) + 1)
928 let altdir = FugitiveExtractGitDir(file)
929 if len(altdir) && !s:cpath(dir, altdir)
930 return fugitive#Find(a:object, altdir)
935 let commits = split(commit, '\.\.\.-\@!', 1)
937 call map(commits, 'empty(v:val) || v:val ==# "@" ? "HEAD" : v:val')
938 let commit = matchstr(s:ChompDefault('', [dir, 'merge-base'] + commits + ['--']), '\<[0-9a-f]\{40,\}\>')
940 if commit !~# '^[0-9a-f]\{40,\}$'
941 let commit = matchstr(s:ChompDefault('', [dir, 'rev-parse', '--verify', commit, '--']), '\<[0-9a-f]\{40,\}\>')
944 let f = 'fugitive://' . dir . '//' . commit . file
946 let f = base . '/' . substitute(rev, '^:/:\=\|^[^:]\+:', '', '')
950 return FugitiveVimPath(f)
953 function! s:Generate(rev, ...) abort
954 return fugitive#Find(a:rev, a:0 ? a:1 : s:Dir())
957 function! s:DotRelative(path, ...) abort
958 let cwd = a:0 ? a:1 : getcwd()
959 let path = substitute(a:path, '^[~$]\i*', '\=expand(submatch(0))', '')
960 if len(cwd) && s:cpath(cwd . '/', (path . '/')[0 : len(cwd)])
961 return '.' . strpart(path, len(cwd))
966 function! fugitive#Object(...) abort
967 let dir = a:0 > 1 ? a:2 : s:Dir()
968 let [fdir, rev] = s:DirRev(a:0 ? a:1 : @%)
969 if s:cpath(dir) !=# s:cpath(fdir)
972 let tree = s:Tree(dir)
973 let full = a:0 ? a:1 : @%
974 let full = fnamemodify(full, ':p' . (s:Slash(full) =~# '/$' ? '' : ':s?/$??'))
975 if empty(rev) && empty(tree)
976 return FugitiveGitPath(full)
978 let rev = fugitive#Path(full, './', dir)
979 if rev =~# '^\./.git\%(/\|$\)'
980 return FugitiveGitPath(full)
983 if rev !~# '^\.\%(/\|$\)' || s:cpath(getcwd(), tree)
986 return FugitiveGitPath(tree . rev[1:-1])
990 let s:var = '\%(%\|#<\=\d\+\|##\=\)'
991 let s:flag = '\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)'
992 let s:expand = '\%(\(' . s:var . '\)\(' . s:flag . '*\)\(:S\)\=\)'
994 function! s:BufName(var) abort
996 return bufname(get(s:TempState(), 'bufnr', ''))
997 elseif a:var =~# '^#\d*$'
998 let nr = get(s:TempState(bufname(+a:var[1:-1])), 'bufnr', '')
999 return bufname(nr ? nr : +a:var[1:-1])
1001 return expand(a:var)
1005 function! s:ExpandVarLegacy(str) abort
1006 if get(g:, 'fugitive_legacy_quoting', 1)
1007 return substitute(a:str, '\\\ze[%#!]', '', 'g')
1013 function! s:ExpandVar(other, var, flags, esc, ...) abort
1014 let cwd = a:0 ? a:1 : getcwd()
1016 return a:other[1:-1]
1017 elseif a:other =~# '^'''
1018 return s:ExpandVarLegacy(substitute(a:other[1:-2], "''", "'", "g"))
1019 elseif a:other =~# '^"'
1020 return s:ExpandVarLegacy(substitute(a:other[1:-2], '""', '"', "g"))
1021 elseif a:other =~# '^!'
1022 let buffer = s:BufName(len(a:other) > 1 ? '#'. a:other[1:-1] : '%')
1023 let owner = s:Owner(buffer)
1024 return len(owner) ? owner : '@'
1027 let file = s:DotRelative(fugitive#Real(s:BufName(a:var)), cwd)
1029 let flag = matchstr(flags, s:flag)
1030 let flags = strpart(flags, len(flag))
1032 let file = s:DotRelative(file, cwd)
1034 let file = fnamemodify(file, flag)
1037 let file = s:Slash(file)
1038 return (len(a:esc) ? shellescape(file) : file)
1041 function! s:Expand(rev, ...) abort
1042 if a:rev =~# '^:[0-3]$'
1043 let file = len(expand('%')) ? a:rev . ':%' : '%'
1044 elseif a:rev ==# '>'
1046 elseif a:rev =~# '^>[~^]'
1047 let file = len(expand('%')) ? '!' . a:rev[1:-1] . ':%' : '%'
1048 elseif a:rev =~# '^>[> ]\@!'
1049 let file = len(expand('%')) ? a:rev[1:-1] . ':%' : '%'
1053 return substitute(file,
1054 \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1055 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),"", a:0 ? a:1 : getcwd())', 'g')
1058 function! fugitive#Expand(object) abort
1059 return substitute(a:object,
1060 \ '\(\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1061 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5))', 'g')
1064 function! s:ExpandSplit(string, ...) abort
1066 let string = a:string
1067 let handle_bar = a:0 && a:1
1068 let dquote = handle_bar ? '"\%([^"]\|""\|\\"\)*"\|' : ''
1069 let cwd = a:0 > 1 ? a:2 : getcwd()
1070 while string =~# '\S'
1071 if handle_bar && string =~# '^\s*|'
1072 return [list, substitute(string, '^\s*', '', '')]
1074 let arg = matchstr(string, '^\s*\%(' . dquote . '''[^'']*''\|\\.\|[^[:space:] ' . (handle_bar ? '|' : '') . ']\)\+')
1075 let string = strpart(string, len(arg))
1076 let arg = substitute(arg, '^\s\+', '', '')
1077 if !exists('seen_separator')
1078 let arg = substitute(arg, '^\%([^:.][^:]*:\|^:\|^:[0-3]:\)\=\zs\.\.\=\%(/.*\)\=$',
1079 \ '\=s:DotRelative(s:Slash(simplify(getcwd() . "/" . submatch(0))), cwd)', '')
1081 let arg = substitute(arg,
1082 \ '\(' . dquote . '''\%(''''\|[^'']\)*''\|\\[' . s:fnameescape . ']\|^\\[>+-]\|!\d*\)\|' . s:expand,
1083 \ '\=s:ExpandVar(submatch(1),submatch(2),submatch(3),submatch(5), cwd)', 'g')
1086 let seen_separator = 1
1089 return handle_bar ? [list, ''] : list
1092 function! s:SplitExpand(string, ...) abort
1093 return s:ExpandSplit(a:string, 0, a:0 ? a:1 : getcwd())
1096 function! s:SplitExpandChain(string, ...) abort
1097 return s:ExpandSplit(a:string, 1, a:0 ? a:1 : getcwd())
1102 function! s:TreeInfo(dir, commit) abort
1103 if a:commit =~# '^:\=[0-3]$'
1104 let index = get(s:indexes, a:dir, [])
1105 let newftime = getftime(fugitive#Find('.git/index', a:dir))
1106 if get(index, 0, -1) < newftime
1107 let [lines, exec_error] = s:LinesError([a:dir, 'ls-files', '--stage', '--'])
1108 let s:indexes[a:dir] = [newftime, {'0': {}, '1': {}, '2': {}, '3': {}}]
1113 let [info, filename] = split(line, "\t")
1114 let [mode, sha, stage] = split(info, '\s\+')
1115 let s:indexes[a:dir][1][stage][filename] = [newftime, mode, 'blob', sha, -2]
1116 while filename =~# '/'
1117 let filename = substitute(filename, '/[^/]*$', '', '')
1118 let s:indexes[a:dir][1][stage][filename] = [newftime, '040000', 'tree', '', 0]
1122 return [get(s:indexes[a:dir][1], a:commit[-1:-1], {}), newftime]
1123 elseif a:commit =~# '^\x\{40,\}$'
1124 if !has_key(s:trees, a:dir)
1125 let s:trees[a:dir] = {}
1127 if !has_key(s:trees[a:dir], a:commit)
1128 let [ftime, exec_error] = s:ChompError([a:dir, 'log', '-1', '--pretty=format:%ct', a:commit, '--'])
1130 let s:trees[a:dir][a:commit] = [{}, -1]
1131 return s:trees[a:dir][a:commit]
1133 let s:trees[a:dir][a:commit] = [{}, +ftime]
1134 let [lines, exec_error] = s:LinesError([a:dir, 'ls-tree', '-rtl', '--full-name', a:commit, '--'])
1136 return s:trees[a:dir][a:commit]
1139 let [info, filename] = split(line, "\t")
1140 let [mode, type, sha, size] = split(info, '\s\+')
1141 let s:trees[a:dir][a:commit][0][filename] = [+ftime, mode, type, sha, +size, filename]
1144 return s:trees[a:dir][a:commit]
1149 function! s:PathInfo(url) abort
1150 let [dir, commit, file] = s:DirCommitFile(a:url)
1151 if empty(dir) || !get(g:, 'fugitive_file_api', 1)
1152 return [-1, '000000', '', '', -1]
1154 let path = substitute(file[1:-1], '/*$', '', '')
1155 let [tree, ftime] = s:TreeInfo(dir, commit)
1156 let entry = empty(path) ? [ftime, '040000', 'tree', '', -1] : get(tree, path, [])
1157 if empty(entry) || file =~# '/$' && entry[2] !=# 'tree'
1158 return [-1, '000000', '', '', -1]
1164 function! fugitive#simplify(url) abort
1165 let [dir, commit, file] = s:DirCommitFile(a:url)
1169 if file =~# '/\.\.\%(/\|$\)'
1170 let tree = s:Tree(dir)
1172 let path = simplify(tree . file)
1173 if strpart(path . '/', 0, len(tree) + 1) !=# tree . '/'
1174 return FugitiveVimPath(path)
1178 return FugitiveVimPath('fugitive://' . simplify(dir) . '//' . commit . simplify(file))
1181 function! fugitive#resolve(url) abort
1182 let url = fugitive#simplify(a:url)
1183 if url =~? '^fugitive:'
1190 function! fugitive#getftime(url) abort
1191 return s:PathInfo(a:url)[0]
1194 function! fugitive#getfsize(url) abort
1195 let entry = s:PathInfo(a:url)
1196 if entry[4] == -2 && entry[2] ==# 'blob' && len(entry[3])
1197 let dir = s:DirCommitFile(a:url)[0]
1198 let entry[4] = +s:ChompDefault(-1, [dir, 'cat-file', '-s', entry[3]])
1203 function! fugitive#getftype(url) abort
1204 return get({'tree': 'dir', 'blob': 'file'}, s:PathInfo(a:url)[2], '')
1207 function! fugitive#filereadable(url) abort
1208 return s:PathInfo(a:url)[2] ==# 'blob'
1211 function! fugitive#filewritable(url) abort
1212 let [dir, commit, file] = s:DirCommitFile(a:url)
1213 if commit !~# '^\d$' || !filewritable(fugitive#Find('.git/index', dir))
1216 return s:PathInfo(a:url)[2] ==# 'blob' ? 1 : 2
1219 function! fugitive#isdirectory(url) abort
1220 return s:PathInfo(a:url)[2] ==# 'tree'
1223 function! fugitive#getfperm(url) abort
1224 let [dir, commit, file] = s:DirCommitFile(a:url)
1225 let perm = getfperm(dir)
1226 let fperm = s:PathInfo(a:url)[1]
1227 if fperm ==# '040000'
1228 let fperm = '000755'
1231 let perm = tr(perm, 'x', '-')
1233 if fperm !~# '[45]$'
1234 let perm = tr(perm, 'rw', '--')
1236 if commit !~# '^\d$'
1237 let perm = tr(perm, 'w', '-')
1239 return perm ==# '---------' ? '' : perm
1242 function! fugitive#setfperm(url, perm) abort
1243 let [dir, commit, file] = s:DirCommitFile(a:url)
1244 let entry = s:PathInfo(a:url)
1245 let perm = fugitive#getfperm(a:url)
1246 if commit !~# '^\d$' || entry[2] !=# 'blob' ||
1247 \ substitute(perm, 'x', '-', 'g') !=# substitute(a:perm, 'x', '-', 'g')
1250 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1251 \ (a:perm =~# 'x' ? '000755 ' : '000644 ') . entry[3] . ' ' . commit . "\t" . file[1:-1])[1]
1252 return exec_error ? -1 : 0
1255 function! s:TempCmd(out, cmd) abort
1258 let cmd = (type(a:cmd) == type([]) ? fugitive#Prepare(a:cmd) : a:cmd)
1259 let redir = ' > ' . a:out
1261 let cmd_escape_char = &shellxquote == '(' ? '^' : '^^^'
1262 return s:SystemError('cmd /c "' . prefix . s:gsub(cmd, '[<>]', cmd_escape_char . '&') . redir . '"')
1263 elseif &shell =~# 'fish'
1264 return s:SystemError(' begin;' . prefix . cmd . redir . ';end ')
1266 return s:SystemError(' (' . prefix . cmd . redir . ') ')
1271 if !exists('s:blobdirs')
1274 function! s:BlobTemp(url) abort
1275 let [dir, commit, file] = s:DirCommitFile(a:url)
1279 if !has_key(s:blobdirs, dir)
1280 let s:blobdirs[dir] = tempname()
1282 let tempfile = s:blobdirs[dir] . '/' . commit . file
1283 let tempparent = fnamemodify(tempfile, ':h')
1284 if !isdirectory(tempparent)
1285 call mkdir(tempparent, 'p')
1287 if commit =~# '^\d$' || !filereadable(tempfile)
1288 let rev = s:DirRev(a:url)[1]
1289 let exec_error = s:TempCmd(tempfile, [dir, 'cat-file', 'blob', rev])[1]
1291 call delete(tempfile)
1295 return s:Resolve(tempfile)
1298 function! fugitive#readfile(url, ...) abort
1299 let entry = s:PathInfo(a:url)
1300 if entry[2] !=# 'blob'
1303 let temp = s:BlobTemp(a:url)
1307 return call('readfile', [temp] + a:000)
1310 function! fugitive#writefile(lines, url, ...) abort
1311 let url = type(a:url) ==# type('') ? a:url : ''
1312 let [dir, commit, file] = s:DirCommitFile(url)
1313 let entry = s:PathInfo(url)
1314 if commit =~# '^\d$' && entry[2] !=# 'tree'
1315 let temp = tempname()
1316 if a:0 && a:1 =~# 'a' && entry[2] ==# 'blob'
1317 call writefile(fugitive#readfile(url, 'b'), temp, 'b')
1319 call call('writefile', [a:lines, temp] + a:000)
1320 let [hash, exec_error] = s:ChompError([dir, 'hash-object', '-w', temp])
1321 let mode = len(entry[1]) ? entry[1] : '100644'
1322 if !exec_error && hash =~# '^\x\{40,\}$'
1323 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1324 \ mode . ' ' . hash . ' ' . commit . "\t" . file[1:-1])[1]
1330 return call('writefile', [a:lines, a:url] + a:000)
1334 \ '/**/': '/\%([^./][^/]*/\)*',
1335 \ '/**': '/\%([^./][^/]\+/\)*[^./][^/]*',
1336 \ '**/': '[^/]*\%(/[^./][^/]*\)*',
1338 \ '/*': '/[^/.][^/]*',
1341 function! fugitive#glob(url, ...) abort
1342 let [dirglob, commit, glob] = s:DirCommitFile(a:url)
1343 let append = matchstr(glob, '/*$')
1344 let glob = substitute(glob, '/*$', '', '')
1345 let pattern = '^' . substitute(glob, '/\=\*\*/\=\|/\=\*\|[.?\$]\|^^', '\=get(s:globsubs, submatch(0), "\\" . submatch(0))', 'g')[1:-1] . '$'
1347 for dir in dirglob =~# '[*?]' ? split(glob(dirglob), "\n") : [dirglob]
1348 if empty(dir) || !get(g:, 'fugitive_file_api', 1) || !filereadable(fugitive#Find('.git/HEAD', dir))
1351 let files = items(s:TreeInfo(dir, commit)[0])
1353 call filter(files, 'v:val[1][2] ==# "tree"')
1355 call map(files, 'v:val[0]')
1356 call filter(files, 'v:val =~# pattern')
1357 let prepend = 'fugitive://' . dir . '//' . substitute(commit, '^:', '', '') . '/'
1359 call map(files, 'FugitiveVimPath(prepend . v:val . append)')
1360 call extend(results, files)
1365 return join(results, "\n")
1369 function! fugitive#delete(url, ...) abort
1370 let [dir, commit, file] = s:DirCommitFile(a:url)
1371 if a:0 && len(a:1) || commit !~# '^\d$'
1374 let entry = s:PathInfo(a:url)
1375 if entry[2] !=# 'blob'
1378 let exec_error = s:SystemError([dir, 'update-index', '--index-info'],
1379 \ '000000 0000000000000000000000000000000000000000 ' . commit . "\t" . file[1:-1])[1]
1380 return exec_error ? -1 : 0
1383 " Section: Buffer Object
1385 let s:buffer_prototype = {}
1387 function! fugitive#buffer(...) abort
1388 let buffer = {'#': bufnr(a:0 ? a:1 : '%')}
1389 call extend(buffer, s:buffer_prototype, 'keep')
1393 function! s:buffer_repo() dict abort
1394 return fugitive#repo(self['#'])
1397 function! s:buffer_type(...) dict abort
1398 return 'see b:fugitive_type'
1401 call s:add_methods('buffer', ['repo', 'type'])
1403 " Section: Completion
1405 function! s:FilterEscape(items, ...) abort
1406 let items = copy(a:items)
1407 if a:0 && type(a:1) == type('')
1408 call filter(items, 'strpart(v:val, 0, strlen(a:1)) ==# a:1')
1410 return map(items, 's:fnameescape(v:val)')
1413 function! s:GlobComplete(lead, pattern) abort
1416 elseif v:version >= 704
1417 let results = glob(a:lead . a:pattern, 0, 1)
1419 let results = split(glob(a:lead . a:pattern), "\n")
1421 call map(results, 'v:val !~# "/$" && isdirectory(v:val) ? v:val."/" : v:val')
1422 call map(results, 'v:val[ strlen(a:lead) : -1 ]')
1426 function! fugitive#CompletePath(base, ...) abort
1427 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1428 let tree = s:Tree(dir) . '/'
1429 let strip = '^\%(:/:\=\|:(top)\|:(top,literal)\|:(literal,top)\|:(literal)\)'
1430 let base = substitute(a:base, strip, '', '')
1431 if base =~# '^\.git/'
1432 let pattern = s:gsub(base[5:-1], '/', '*&').'*'
1433 let matches = s:GlobComplete(dir . '/', pattern)
1434 let cdir = fugitive#CommonDir(dir)
1435 if len(cdir) && s:cpath(dir) !=# s:cpath(cdir)
1436 call extend(matches, s:GlobComplete(cdir . '/', pattern))
1438 call s:Uniq(matches)
1439 call map(matches, "'.git/' . v:val")
1440 elseif base =~# '^\~/'
1441 let matches = map(s:GlobComplete(expand('~/'), base[2:-1] . '*'), '"~/" . v:val')
1442 elseif a:base =~# '^/\|^\a\+:\|^\.\.\=/\|^:(literal)'
1443 let matches = s:GlobComplete('', base . '*')
1444 elseif len(tree) > 1
1445 let matches = s:GlobComplete(tree, s:gsub(base, '/', '*&').'*')
1449 call map(matches, 's:fnameescape(s:Slash(matchstr(a:base, strip) . v:val))')
1453 function! fugitive#PathComplete(...) abort
1454 return call('fugitive#CompletePath', a:000)
1457 function! fugitive#CompleteObject(base, ...) abort
1458 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
1460 let tree = s:Tree(dir) . '/'
1462 if len(tree) > 1 && s:cpath(tree, cwd[0 : len(tree) - 1])
1463 let subdir = strpart(cwd, len(tree)) . '/'
1466 if a:base =~# '^\.\=/\|^:(' || a:base !~# ':'
1468 if a:base =~# '^refs/'
1469 let results += map(s:GlobComplete(fugitive#CommonDir(dir) . '/', a:base . '*'), 's:Slash(v:val)')
1470 elseif a:base !~# '^\.\=/\|^:('
1471 let heads = ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'refs/']
1472 let heads += sort(s:LinesError(["rev-parse","--symbolic","--branches","--tags","--remotes"], dir)[0])
1473 if filereadable(fugitive#Find('.git/refs/stash', dir))
1474 let heads += ["stash"]
1475 let heads += sort(s:LinesError(["stash","list","--pretty=format:%gd"], dir)[0])
1477 call filter(heads,'v:val[ 0 : strlen(a:base)-1 ] ==# a:base')
1478 let results += heads
1480 call map(results, 's:fnameescape(v:val)')
1482 let results += a:0 == 1 ? fugitive#CompletePath(a:base, dir) : fugitive#CompletePath(a:base)
1486 elseif a:base =~# '^:'
1487 let entries = s:LinesError(['ls-files','--stage'], dir)[0]
1488 if a:base =~# ':\./'
1489 call map(entries, 'substitute(v:val, "\\M\t\\zs" . subdir, "./", "")')
1491 call map(entries,'s:sub(v:val,".*(\\d)\\t(.*)",":\\1:\\2")')
1492 if a:base !~# '^:[0-3]\%(:\|$\)'
1493 call filter(entries,'v:val[1] == "0"')
1494 call map(entries,'v:val[2:-1]')
1498 let tree = matchstr(a:base, '.*[:/]')
1499 let entries = s:LinesError(['ls-tree', substitute(tree, ':\zs\./', '\=subdir', '')], dir)[0]
1500 call map(entries,'s:sub(v:val,"^04.*\\zs$","/")')
1501 call map(entries,'tree.s:sub(v:val,".*\t","")')
1504 return s:FilterEscape(entries, a:base)
1507 function! s:CompleteSub(subcommand, A, L, P, ...) abort
1508 let pre = strpart(a:L, 0, a:P)
1510 return fugitive#CompletePath(a:A)
1511 elseif a:A =~# '^-' || a:A is# 0
1512 return s:FilterEscape(split(s:ChompDefault('', a:subcommand, '--git-completion-helper'), ' '), a:A)
1514 return fugitive#CompleteObject(a:A, s:Dir())
1515 elseif type(a:1) == type(function('tr'))
1516 return call(a:1, [a:A, a:L, a:P])
1518 return s:FilterEscape(a:1, a:A)
1522 function! s:CompleteRevision(A, L, P) abort
1523 return s:FilterEscape(['HEAD', 'FETCH_HEAD', 'MERGE_HEAD', 'ORIG_HEAD'] +
1524 \ s:LinesError('rev-parse', '--symbolic', '--branches', '--tags', '--remotes')[0], a:A)
1527 function! s:CompleteRemote(A, L, P) abort
1528 let remote = matchstr(a:L, '\u\w*[! ] *\zs\S\+\ze ')
1530 let matches = s:LinesError('ls-remote', remote)[0]
1531 call filter(matches, 'v:val =~# "\t" && v:val !~# "{"')
1532 call map(matches, 's:sub(v:val, "^.*\t%(refs/%(heads/|tags/)=)=", "")')
1534 let matches = s:LinesError('remote')[0]
1536 return s:FilterEscape(matches, a:A)
1539 " Section: Buffer auto-commands
1541 function! s:ReplaceCmd(cmd) abort
1542 let temp = tempname()
1543 let [err, exec_error] = s:TempCmd(temp, a:cmd)
1545 call s:throw((len(err) ? err : filereadable(temp) ? join(readfile(temp), ' ') : 'unknown error running ' . a:cmd))
1547 let temp = s:Resolve(temp)
1548 let fn = expand('%:p')
1549 silent exe 'keepalt file '.temp
1550 let modelines = &modelines
1553 silent keepjumps noautocmd edit!
1555 let &modelines = modelines
1557 silent exe 'keepalt file '.s:fnameescape(fn)
1558 catch /^Vim\%((\a\+)\)\=:E302:/
1561 if s:cpath(fnamemodify(bufname('$'), ':p'), temp)
1562 silent execute 'bwipeout '.bufnr('$')
1567 function! s:QueryLog(refspec) abort
1568 let lines = s:LinesError(['log', '-n', '256', '--format=%h%x09%s', a:refspec, '--'])[0]
1569 call map(lines, 'split(v:val, "\t")')
1570 call map(lines, '{"type": "Log", "commit": v:val[0], "subject": v:val[-1]}')
1574 function! s:FormatLog(dict) abort
1575 return a:dict.commit . ' ' . a:dict.subject
1578 function! s:FormatRebase(dict) abort
1579 return a:dict.status . ' ' . a:dict.commit . ' ' . a:dict.subject
1582 function! s:FormatFile(dict) abort
1583 return a:dict.status . ' ' . a:dict.filename
1586 function! s:Format(val) abort
1587 if type(a:val) == type({})
1588 return s:Format{a:val.type}(a:val)
1589 elseif type(a:val) == type([])
1590 return map(copy(a:val), 's:Format(v:val)')
1596 function! s:AddHeader(key, value) abort
1601 while !empty(getline(before))
1604 call append(before - 1, [a:key . ':' . (len(a:value) ? ' ' . a:value : '')])
1605 if before == 1 && line('$') == 2
1606 silent keepjumps 2delete _
1610 function! s:AddSection(label, lines, ...) abort
1611 let note = a:0 ? a:1 : ''
1612 if empty(a:lines) && empty(note)
1615 call append(line('$'), ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines))
1618 function! fugitive#BufReadStatus() abort
1619 let amatch = s:Slash(expand('%:p'))
1620 let b:fugitive_type = 'index'
1621 unlet! b:fugitive_reltime
1623 silent doautocmd BufReadPre
1624 let cmd = [fnamemodify(amatch, ':h')]
1625 setlocal noro ma nomodeline buftype=nowrite
1626 if s:cpath(fnamemodify($GIT_INDEX_FILE !=# '' ? $GIT_INDEX_FILE : fugitive#Find('.git/index'), ':p')) !=# s:cpath(amatch)
1627 let cmd += ['-c', 'GIT_INDEX_FILE=' . amatch]
1629 let cmd += ['status', '--porcelain', '-bz']
1630 let [output, message, exec_error] = s:NullError(cmd)
1632 throw 'fugitive: ' . message
1635 let head = matchstr(output[0], '^## \zs\S\+\ze\%($\| \[\)')
1637 if head =~# '\.\.\.'
1638 let [head, pull] = split(head, '\.\.\.')
1640 elseif head ==# 'HEAD' || empty(head)
1641 let head = FugitiveHead(11)
1647 let b:fugitive_status = {'Staged': {}, 'Unstaged': {}}
1648 let [staged, unstaged, untracked] = [[], [], []]
1650 while i < len(output)
1651 let line = output[i]
1652 let file = line[3:-1]
1658 if line[0:1] =~# '[RC]'
1659 let files = output[i] . ' -> ' . file
1662 if line[0] !~# '[ ?!#]'
1663 call add(staged, {'type': 'File', 'status': line[0], 'filename': files})
1665 if line[0:1] ==# '??'
1666 call add(untracked, {'type': 'File', 'status': line[1], 'filename': files})
1667 elseif line[1] !~# '[ !#]'
1668 call add(unstaged, {'type': 'File', 'status': line[1], 'filename': files})
1673 let b:fugitive_status['Staged'][dict.filename] = dict.status
1675 for dict in unstaged
1676 let b:fugitive_status['Unstaged'][dict.filename] = dict.status
1679 let config = fugitive#Config()
1681 let pull_type = 'Pull'
1683 let rebase = fugitive#Config('branch.' . branch . '.rebase', config)
1685 let rebase = fugitive#Config('pull.rebase', config)
1687 if rebase =~# '^\%(true\|yes\|on\|1\|interactive\)$'
1688 let pull_type = 'Rebase'
1689 elseif rebase =~# '^\%(false\|no|off\|0\|\)$'
1690 let pull_type = 'Merge'
1694 let push_remote = fugitive#Config('branch.' . branch . '.pushRemote', config)
1695 if empty(push_remote)
1696 let push_remote = fugitive#Config('remote.pushDefault', config)
1698 let push = len(push_remote) && len(branch) ? push_remote . '/' . branch : ''
1704 let unpulled = s:QueryLog(head . '..' . pull)
1709 let unpushed = s:QueryLog(push . '..' . head)
1714 if isdirectory(fugitive#Find('.git/rebase-merge/'))
1715 let rebasing_dir = fugitive#Find('.git/rebase-merge/')
1716 elseif isdirectory(fugitive#Find('.git/rebase-apply/'))
1717 let rebasing_dir = fugitive#Find('.git/rebase-apply/')
1721 let rebasing_head = 'detached HEAD'
1722 if exists('rebasing_dir') && filereadable(rebasing_dir . 'git-rebase-todo')
1723 let rebasing_head = substitute(readfile(rebasing_dir . 'head-name')[0], '\C^refs/heads/', '', '')
1725 let lines = readfile(rebasing_dir . 'git-rebase-todo')
1727 let hash = matchstr(line, '^[^a-z].*\s\zs[0-9a-f]\{4,\}\ze\.\.')
1733 if getfsize(rebasing_dir . 'done') > 0
1734 let done = readfile(rebasing_dir . 'done')
1735 call map(done, 'substitute(v:val, ''^\l\+\>'', "done", "")')
1736 let done[-1] = substitute(done[-1], '^\l\+\>', 'stop', '')
1737 let lines = done + lines
1741 let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
1742 if len(match) && match[1] !~# 'exec\|merge\|label'
1743 call add(rebasing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': strpart(match[2], 0, len), 'subject': match[3]})
1748 let diff = {'Staged': [], 'Unstaged': []}
1750 let diff['Staged'] =
1751 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix', '--cached'])[0]
1754 let diff['Unstaged'] =
1755 \ s:LinesError(['diff', '--color=never', '--no-ext-diff', '--no-prefix'])[0]
1757 let b:fugitive_diff = diff
1758 let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}})
1759 let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}}
1761 silent keepjumps %delete_
1763 call s:AddHeader('Head', head)
1764 call s:AddHeader(pull_type, pull)
1766 call s:AddHeader('Push', push)
1768 call s:AddSection('Rebasing ' . rebasing_head, rebasing)
1769 call s:AddSection('Untracked', untracked)
1770 call s:AddSection('Unstaged', unstaged)
1771 let unstaged_end = len(unstaged) ? line('$') : 0
1772 call s:AddSection('Staged', staged)
1773 let staged_end = len(staged) ? line('$') : 0
1774 call s:AddSection('Unpushed to ' . push, unpushed)
1775 call s:AddSection('Unpulled from ' . pull, unpulled)
1777 setlocal nomodified readonly noswapfile
1778 silent doautocmd BufReadPost
1779 setlocal nomodifiable
1780 if &bufhidden ==# ''
1781 setlocal bufhidden=delete
1783 let b:dispatch = ':Gfetch --all'
1784 call fugitive#MapJumps()
1785 call s:Map('n', '-', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1786 call s:Map('x', '-', ":<C-U>execute <SID>Do('Toggle',1)<CR>", '<silent>')
1787 call s:Map('n', 's', ":<C-U>execute <SID>Do('Stage',0)<CR>", '<silent>')
1788 call s:Map('x', 's', ":<C-U>execute <SID>Do('Stage',1)<CR>", '<silent>')
1789 call s:Map('n', 'u', ":<C-U>execute <SID>Do('Unstage',0)<CR>", '<silent>')
1790 call s:Map('x', 'u', ":<C-U>execute <SID>Do('Unstage',1)<CR>", '<silent>')
1791 call s:Map('n', 'U', ":exe <SID>EchoExec('reset', '-q')<CR>", '<silent>')
1792 call s:MapMotion('gu', "exe <SID>StageJump(v:count, 'Untracked', 'Unstaged')")
1793 call s:MapMotion('gU', "exe <SID>StageJump(v:count, 'Unstaged', 'Untracked')")
1794 call s:MapMotion('gs', "exe <SID>StageJump(v:count, 'Staged')")
1795 call s:MapMotion('gp', "exe <SID>StageJump(v:count, 'Unpushed')")
1796 call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
1797 call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
1798 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>')
1799 call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
1800 call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
1801 call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
1802 call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
1803 call s:Map('n', ">", ":<C-U>execute <SID>StageInline('show', line('.'),v:count)<CR>", '<silent>')
1804 call s:Map('x', "=", ":<C-U>execute <SID>StageInline('toggle',line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1805 call s:Map('x', "<", ":<C-U>execute <SID>StageInline('hide', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1806 call s:Map('x', ">", ":<C-U>execute <SID>StageInline('show', line(\"'<\"),line(\"'>\")-line(\"'<\")+1)<CR>", '<silent>')
1807 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>')
1808 call s:Map('n', 'dd', ":<C-U>execute <SID>StageDiff('Gdiffsplit')<CR>", '<silent>')
1809 call s:Map('n', 'dh', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1810 call s:Map('n', 'ds', ":<C-U>execute <SID>StageDiff('Ghdiffsplit')<CR>", '<silent>')
1811 call s:Map('n', 'dp', ":<C-U>execute <SID>StageDiffEdit()<CR>", '<silent>')
1812 call s:Map('n', 'dv', ":<C-U>execute <SID>StageDiff('Gvdiffsplit')<CR>", '<silent>')
1813 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
1814 call s:Map('n', 'P', ":<C-U>execute <SID>StagePatch(line('.'),line('.')+v:count1-1)<CR>", '<silent>')
1815 call s:Map('x', 'P', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1816 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>')
1817 call s:Map('x', 'p', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1818 call s:Map('n', 'I', ":<C-U>execute <SID>StagePatch(line('.'),line('.'))<CR>", '<silent>')
1819 call s:Map('x', 'I', ":<C-U>execute <SID>StagePatch(line(\"'<\"),line(\"'>\"))<CR>", '<silent>')
1820 if empty(mapcheck('q', 'n'))
1821 nnoremap <buffer> <silent> q :<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<Bar>echohl WarningMsg<Bar>echo ':Gstatus q is deprecated in favor of gq or the built-in <Lt>C-W>q'<Bar>echohl NONE<CR>
1823 call s:Map('n', 'gq', ":<C-U>if bufnr('$') == 1<Bar>quit<Bar>else<Bar>bdelete<Bar>endif<CR>", '<silent>')
1824 call s:Map('n', 'R', ":echohl WarningMsg<Bar>echo 'Reloading is automatic. Use :e to force'<Bar>echohl NONE<CR>", '<silent>')
1825 call s:Map('n', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1826 call s:Map('x', 'g<Bar>', ":<C-U>echoerr 'Changed to X'<CR>", '<silent>')
1827 call s:Map('n', 'X', ":<C-U>execute <SID>StageDelete(line('.'), 0, v:count)<CR>", '<silent>')
1828 call s:Map('x', 'X', ":<C-U>execute <SID>StageDelete(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1829 call s:Map('n', 'gI', ":<C-U>execute <SID>StageIgnore(line('.'), line('.'), v:count)<CR>", '<silent>')
1830 call s:Map('x', 'gI', ":<C-U>execute <SID>StageIgnore(line(\"'<\"), line(\"'>\"), v:count)<CR>", '<silent>')
1831 call s:Map('n', '.', ':<C-U> <C-R>=<SID>StageArgs(0)<CR><Home>')
1832 call s:Map('x', '.', ':<C-U> <C-R>=<SID>StageArgs(1)<CR><Home>')
1833 setlocal filetype=fugitive
1835 for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']]
1836 while len(getline(lnum))
1837 let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*')
1838 if has_key(expanded[section], filename)
1839 call s:StageInline('show', lnum)
1845 let b:fugitive_reltime = reltime()
1848 return 'echoerr ' . string(v:exception)
1852 function! fugitive#FileReadCmd(...) abort
1853 let amatch = a:0 ? a:1 : expand('<amatch>')
1854 let [dir, rev] = s:DirRev(amatch)
1855 let line = a:0 > 1 ? a:2 : line("'[")
1857 return 'noautocmd ' . line . 'read ' . s:fnameescape(amatch)
1859 if rev !~# ':' && s:ChompDefault('', [dir, 'cat-file', '-t', rev]) =~# '^\%(commit\|tag\)$'
1860 let cmd = fugitive#Prepare(dir, 'log', '--pretty=format:%B', '-1', rev, '--')
1862 let cmd = fugitive#Prepare(dir, 'cat-file', '-p', rev)
1864 return line . 'read !' . escape(cmd, '!#%')
1867 function! fugitive#FileWriteCmd(...) abort
1868 let tmp = tempname()
1869 let amatch = a:0 ? a:1 : expand('<amatch>')
1870 let autype = a:0 > 1 ? 'Buf' : 'File'
1871 if exists('#' . autype . 'WritePre')
1872 execute 'doautocmd ' . autype . 'WritePre ' . s:fnameescape(amatch)
1875 let [dir, commit, file] = s:DirCommitFile(amatch)
1876 if commit !~# '^[0-3]$' || !v:cmdbang && (line("'[") != 1 || line("']") != line('$'))
1877 return "noautocmd '[,']write" . (v:cmdbang ? '!' : '') . ' ' . s:fnameescape(amatch)
1879 silent execute "'[,']write !".fugitive#Prepare(dir, 'hash-object', '-w', '--stdin', '--').' > '.tmp
1880 let sha1 = readfile(tmp)[0]
1881 let old_mode = matchstr(s:SystemError([dir, 'ls-files', '--stage', '.' . file])[0], '^\d\+')
1883 let old_mode = executable(s:Tree(dir) . file) ? '100755' : '100644'
1885 let info = old_mode.' '.sha1.' '.commit."\t".file[1:-1]
1886 let [error, exec_error] = s:SystemError([dir, 'update-index', '--index-info'], info . "\n")
1889 if exists('#' . autype . 'WritePost')
1890 execute 'doautocmd ' . autype . 'WritePost ' . s:fnameescape(amatch)
1894 return 'echoerr '.string('fugitive: '.error)
1901 let s:nomodeline = (v:version >= 704 ? '<nomodeline>' : '')
1903 function! fugitive#BufReadCmd(...) abort
1904 let amatch = a:0 ? a:1 : expand('<amatch>')
1906 let [dir, rev] = s:DirRev(amatch)
1908 return 'echo "Invalid Fugitive URL"'
1911 let b:fugitive_type = 'stage'
1913 let [b:fugitive_type, exec_error] = s:ChompError([dir, 'cat-file', '-t', rev])
1914 if exec_error && rev =~# '^:0'
1915 let sha = s:ChompDefault('', dir, 'write-tree', '--prefix=' . rev[3:-1])
1916 let exec_error = empty(sha)
1917 let b:fugitive_type = exec_error ? '' : 'tree'
1920 let error = b:fugitive_type
1921 unlet b:fugitive_type
1923 if empty(&bufhidden)
1924 setlocal bufhidden=delete
1927 let &l:readonly = !filewritable(fugitive#Find('.git/index', dir))
1928 return 'silent doautocmd BufNewFile'
1930 setlocal readonly nomodifiable
1931 return 'silent doautocmd BufNewFile|echo ' . string(error)
1933 elseif b:fugitive_type !~# '^\%(tag\|commit\|tree\|blob\)$'
1934 return "echoerr ".string("fugitive: unrecognized git type '".b:fugitive_type."'")
1936 if !exists('b:fugitive_display_format') && b:fugitive_type != 'blob'
1937 let b:fugitive_display_format = +getbufvar('#','fugitive_display_format')
1941 if b:fugitive_type !=# 'blob'
1945 setlocal noreadonly modifiable
1946 let pos = getpos('.')
1947 silent keepjumps %delete_
1951 silent doautocmd BufReadPre
1952 if b:fugitive_type ==# 'tree'
1953 let b:fugitive_display_format = b:fugitive_display_format % 2
1954 if b:fugitive_display_format
1955 call s:ReplaceCmd([dir, 'ls-tree', exists('sha') ? sha : rev])
1958 let sha = s:TreeChomp(dir, 'rev-parse', '--verify', rev, '--')
1960 call s:ReplaceCmd([dir, 'show', '--no-color', sha])
1962 elseif b:fugitive_type ==# 'tag'
1963 let b:fugitive_display_format = b:fugitive_display_format % 2
1964 if b:fugitive_display_format
1965 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1967 call s:ReplaceCmd([dir, 'cat-file', '-p', rev])
1969 elseif b:fugitive_type ==# 'commit'
1970 let b:fugitive_display_format = b:fugitive_display_format % 2
1971 if b:fugitive_display_format
1972 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1974 call s:ReplaceCmd([dir, 'show', '--no-color', '-m', '--first-parent', '--pretty=format:tree%x20%T%nparent%x20%P%nauthor%x20%an%x20<%ae>%x20%ad%ncommitter%x20%cn%x20<%ce>%x20%cd%nencoding%x20%e%n%n%s%n%n%b', rev])
1975 keepjumps call search('^parent ')
1976 if getline('.') ==# 'parent '
1977 silent keepjumps delete_
1979 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps s/\m\C\%(^parent\)\@<! /\rparent /e' . (&gdefault ? '' : 'g')
1981 keepjumps let lnum = search('^encoding \%(<unknown>\)\=$','W',line('.')+3)
1983 silent keepjumps delete_
1985 silent exe (exists(':keeppatterns') ? 'keeppatterns' : '') 'keepjumps 1,/^diff --git\|\%$/s/\r$//e'
1988 elseif b:fugitive_type ==# 'stage'
1989 call s:ReplaceCmd([dir, 'ls-files', '--stage'])
1990 elseif b:fugitive_type ==# 'blob'
1991 call s:ReplaceCmd([dir, 'cat-file', b:fugitive_type, rev])
1994 keepjumps call setpos('.',pos)
1995 setlocal nomodified noswapfile
1996 let modifiable = rev =~# '^:.:' && b:fugitive_type !=# 'tree'
1997 let &l:readonly = !modifiable || !filewritable(fugitive#Find('.git/index', dir))
1998 if empty(&bufhidden)
1999 setlocal bufhidden=delete
2001 let &l:modifiable = modifiable
2002 if b:fugitive_type !=# 'blob'
2003 setlocal filetype=git foldmethod=syntax
2004 call s:Map('n', 'a', ":<C-U>let b:fugitive_display_format += v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2005 call s:Map('n', 'i', ":<C-U>let b:fugitive_display_format -= v:count1<Bar>exe fugitive#BufReadCmd(@%)<CR>", '<silent>')
2007 call fugitive#MapJumps()
2011 return 'silent doautocmd' . s:nomodeline .
2012 \ ' BufReadPost' . (modifiable ? '' : '|setl nomodifiable')
2014 return 'echoerr ' . string(v:exception)
2018 function! fugitive#BufWriteCmd(...) abort
2019 return fugitive#FileWriteCmd(a:0 ? a:1 : expand('<amatch>'), 1)
2022 function! fugitive#SourceCmd(...) abort
2023 let amatch = a:0 ? a:1 : expand('<amatch>')
2024 let temp = s:BlobTemp(amatch)
2026 return 'noautocmd source ' . s:fnameescape(amatch)
2028 if !exists('g:virtual_scriptnames')
2029 let g:virtual_scriptnames = {}
2031 let g:virtual_scriptnames[temp] = amatch
2032 return 'source ' . s:fnameescape(temp)
2035 " Section: Temp files
2037 if !exists('s:temp_files')
2038 let s:temp_files = {}
2041 function! s:TempState(...) abort
2042 return get(s:temp_files, s:cpath(fnamemodify(a:0 ? a:1 : @%, ':p')), {})
2045 function! s:TempReadPre(file) abort
2046 if has_key(s:temp_files, s:cpath(a:file))
2047 let dict = s:temp_files[s:cpath(a:file)]
2049 setlocal bufhidden=delete nobuflisted
2050 setlocal buftype=nowrite
2051 if has_key(dict, 'modifiable')
2052 let &l:modifiable = dict.modifiable
2055 let b:git_dir = dict.dir
2056 call extend(b:, {'fugitive_type': 'temp'}, 'keep')
2061 function! s:TempReadPost(file) abort
2062 if has_key(s:temp_files, s:cpath(a:file))
2063 let dict = s:temp_files[s:cpath(a:file)]
2064 if has_key(dict, 'filetype') && dict.filetype !=# &l:filetype
2065 let &l:filetype = dict.filetype
2067 setlocal foldmarker=<<<<<<<,>>>>>>>
2068 if empty(mapcheck('q', 'n'))
2069 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>
2072 call s:Map('n', 'gq', ":<C-U>bdelete<CR>", '<silent> <unique>')
2078 augroup fugitive_temp
2080 autocmd BufReadPre * exe s:TempReadPre( expand('<amatch>:p'))
2081 autocmd BufReadPost * exe s:TempReadPost(expand('<amatch>:p'))
2086 function! fugitive#Command(line1, line2, range, bang, mods, arg) abort
2088 let [args, after] = s:SplitExpandChain(a:arg, s:Tree(dir))
2090 let cmd = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
2091 return (empty(cmd) ? 'exe' : cmd) . after
2093 let alias = get(s:Aliases(dir), args[0], '!')
2094 if get(args, 1, '') !=# '--help' && alias !~# '^!\|[\"'']' && !filereadable(s:ExecPath() . '/git-' . args[0])
2095 \ && !(has('win32') && filereadable(s:ExecPath() . '/git-' . args[0] . '.exe'))
2096 call remove(args, 0)
2097 call extend(args, split(alias, '\s\+'), 'keep')
2099 let name = substitute(args[0], '\%(^\|-\)\(\l\)', '\u\1', 'g')
2100 if exists('*s:' . name . 'Subcommand') && get(args, 1, '') !=# '--help'
2103 return 'exe ' . string(s:{name}Subcommand(a:line1, a:line2, a:range, a:bang, a:mods, args[1:-1])) . after
2105 return 'echoerr ' . string(v:exception)
2108 if a:bang || args[0] =~# '^-P$\|^--no-pager$\|diff\%(tool\)\@!\|log\|^show$' ||
2109 \ (args[0] ==# 'stash' && get(args, 1, '') ==# 'show') ||
2110 \ (args[0] ==# 'help' || get(args, 1, '') ==# '--help') && !s:HasOpt(args, '--web')
2111 return s:OpenExec((a:line2 > 0 ? a:line2 : '') . (a:line2 ? 'split' : 'edit'), a:mods, args, dir) . after
2113 if s:HasOpt(args, ['add', 'checkout', 'commit', 'stage', 'stash', 'reset'], '-p', '--patch') ||
2114 \ s:HasOpt(args, ['add', 'clean', 'stage'], '-i', '--interactive') ||
2115 \ index(['--paginate', '-p'], args[0]) >= 0
2116 let mods = substitute(s:Mods(a:mods), '\<tab\>', '-tab', 'g')
2117 let assign = len(dir) ? '|let b:git_dir = ' . string(dir) : ''
2119 if &autowrite || &autowriteall | silent! wall | endif
2120 return mods . (a:line2 ? 'split' : 'edit') . ' term://' . s:fnameescape(s:UserCommand(dir, args)) . assign . '|startinsert' . after
2121 elseif has('terminal')
2122 if &autowrite || &autowriteall | silent! wall | endif
2123 return 'exe ' . string(mods . 'terminal ' . (a:line2 ? '' : '++curwin ') . join(map(s:UserCommandList(dir) + args, 's:fnameescape(v:val)'))) . assign . after
2126 if has('gui_running') && !has('win32')
2127 call insert(args, '--no-pager')
2130 if has('nvim') && executable('env')
2131 let pre .= 'env GIT_TERMINAL_PROMPT=0 '
2133 return 'exe ' . string('!' . escape(pre . s:UserCommand(dir, args), '!#%')) . after
2136 let s:exec_paths = {}
2137 function! s:ExecPath() abort
2138 if !has_key(s:exec_paths, g:fugitive_git_executable)
2139 let s:exec_paths[g:fugitive_git_executable] = s:sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','')
2141 return s:exec_paths[g:fugitive_git_executable]
2144 function! s:Subcommands() abort
2145 let exec_path = s:ExecPath()
2146 return map(split(glob(exec_path.'/git-*'),"\n"),'s:sub(v:val[strlen(exec_path)+5 : -1],"\\.exe$","")')
2150 function! s:Aliases(dir) abort
2151 if !has_key(s:aliases, a:dir)
2152 let s:aliases[a:dir] = {}
2153 let lines = s:NullError([a:dir, 'config', '-z', '--get-regexp', '^alias[.]'])[0]
2155 let s:aliases[a:dir][matchstr(line, '\.\zs.\{-}\ze\n')] = matchstr(line, '\n\zs.*')
2158 return s:aliases[a:dir]
2161 function! fugitive#Complete(lead, ...) abort
2162 let dir = a:0 == 1 ? a:1 : a:0 == 3 ? a:3 : s:Dir()
2163 let pre = a:0 > 1 ? strpart(a:1, 0, a:2) : ''
2164 let subcmd = matchstr(pre, '\u\w*[! ] *\zs[[:alnum:]-]\+\ze ')
2166 let results = sort(s:Subcommands() + keys(s:Aliases(dir)))
2167 elseif pre =~# ' -- '
2168 return fugitive#CompletePath(a:lead, dir)
2169 elseif a:lead =~# '^-'
2170 let results = split(s:ChompDefault('', dir, subcmd, '--git-completion-helper'), ' ')
2172 return fugitive#CompleteObject(a:lead, dir)
2174 return filter(results, 'strpart(v:val, 0, strlen(a:lead)) ==# a:lead')
2177 " Section: :Gcd, :Glcd
2179 function! fugitive#CdComplete(A, L, P) abort
2180 return filter(fugitive#CompletePath(a:A), 'v:val =~# "/$"')
2183 function! fugitive#Cd(path, ...) abort
2184 let path = substitute(a:path, '^:/:\=\|^:(\%(top\|top,literal\|literal,top\|literal\))', '', '')
2185 if path !~# '^/\|^\a\+:\|^\.\.\=\%(/\|$\)'
2188 let path = (empty(s:Tree(dir)) ? dir : s:Tree(dir)) . '/' . path
2190 return (a:0 && a:1 ? 'lcd ' : 'cd ') . s:fnameescape(FugitiveVimPath(path))
2195 call s:command("-bar -bang -range=-1 -addr=other Gstatus", "Status")
2197 function! s:StatusCommand(line1, line2, range, count, bang, mods, reg, arg, args, ...) abort
2198 let dir = a:0 ? a:1 : s:Dir()
2201 let mods = s:Mods(a:mods, &splitbelow ? 'botright' : 'topleft')
2202 let file = fugitive#Find(':', dir)
2203 let arg = ' +setl\ foldmethod=syntax\ foldlevel=1\|let\ w:fugitive_status=FugitiveGitDir() ' .
2204 \ s:fnameescape(file)
2205 for winnr in range(1, winnr('$'))
2206 if s:cpath(file, fnamemodify(bufname(winbufnr(winnr)), ':p'))
2208 call s:ReloadStatus()
2210 call s:ExpireStatus(dir)
2211 exe winnr . 'wincmd w'
2213 let w:fugitive_status = dir
2219 return mods . 'edit' . (a:bang ? '!' : '') . arg
2221 return mods . 'pedit' . arg . '|wincmd P'
2223 return mods . (a:count > 0 ? a:count : '') . 'split' . arg
2226 return 'echoerr ' . string(v:exception)
2231 function! s:StageJump(offset, section, ...) abort
2232 let line = search('^\%(' . a:section . '\)', 'nw')
2234 let line = search('^\%(' . a:1 . '\)', 'nw')
2239 for i in range(a:offset)
2240 call search(s:file_commit_pattern . '\|^$', 'W')
2241 if empty(getline('.')) && a:0 && getline(line('.') + 1) =~# '^\%(' . a:1 . '\)'
2242 call search(s:file_commit_pattern . '\|^$', 'W')
2244 if empty(getline('.'))
2248 call s:StageReveal()
2250 call s:StageReveal()
2257 function! s:StageSeek(info, fallback) abort
2259 if empty(info.section)
2262 let line = search('^' . info.section, 'wn')
2264 for section in get({'Staged': ['Unstaged', 'Untracked'], 'Unstaged': ['Untracked', 'Staged'], 'Untracked': ['Unstaged', 'Staged']}, info.section, [])
2265 let line = search('^' . section, 'wn')
2267 return line + (info.index > 0 ? 1 : 0)
2273 while len(getline(line))
2274 let filename = matchstr(getline(line), '^[A-Z?] \zs.*')
2276 \ ((info.filename[-1:-1] ==# '/' && filename[0 : len(info.filename) - 1] ==# info.filename) ||
2277 \ (filename[-1:-1] ==# '/' && filename ==# info.filename[0 : len(filename) - 1]) ||
2278 \ filename ==# info.filename)
2282 if getline(line+1) !~# '^@'
2283 exe s:StageInline('show', line)
2285 if getline(line+1) !~# '^@'
2288 let type = info.sigil ==# '-' ? '-' : '+'
2290 while offset < info.offset
2292 if getline(line) =~# '^@'
2293 let offset = +matchstr(getline(line), type . '\zs\d\+') - 1
2294 elseif getline(line) =~# '^[ ' . type . ']'
2296 elseif getline(line) !~# '^[ @\+-]'
2303 let commit = matchstr(getline(line), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\+')
2304 if len(commit) && commit ==# info.commit
2310 let i += getline(line) !~# '^[ @\+-]'
2313 return exists('backup') ? backup : line - 1
2316 function! s:ReloadStatus(...) abort
2317 call s:ExpireStatus(-1)
2318 if get(b:, 'fugitive_type', '') !=# 'index'
2321 let original_lnum = a:0 ? a:1 : line('.')
2322 let info = s:StageInfo(original_lnum)
2323 call fugitive#BufReadStatus()
2324 exe s:StageSeek(info, original_lnum)
2329 let s:last_time = reltime()
2330 if !exists('s:last_times')
2331 let s:last_times = {}
2334 function! s:ExpireStatus(bufnr) abort
2336 let s:last_time = reltime()
2339 let dir = s:Dir(a:bufnr)
2341 let s:last_times[s:cpath(dir)] = reltime()
2346 function! FugitiveReloadCheck() abort
2347 let t = b:fugitive_reltime
2348 return [t, reltimestr(reltime(s:last_time, t)),
2349 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t))]
2352 function! s:ReloadWinStatus(...) abort
2353 if get(b:, 'fugitive_type', '') !=# 'index' || &modified
2356 if !exists('b:fugitive_reltime')
2357 exe s:ReloadStatus()
2360 let t = b:fugitive_reltime
2361 if reltimestr(reltime(s:last_time, t)) =~# '-\|\d\{10\}\.' ||
2362 \ reltimestr(reltime(get(s:last_times, s:cpath(s:Dir()), t), t)) =~# '-\|\d\{10\}\.'
2363 exe s:ReloadStatus()
2367 function! s:ReloadTabStatus(...) abort
2368 let mytab = tabpagenr()
2369 let tab = a:0 ? a:1 : mytab
2370 for winnr in range(1, tabpagewinnr(tab, '$'))
2371 if getbufvar(tabpagebuflist(tab)[winnr-1], 'fugitive_type') ==# 'index'
2372 execute 'tabnext '.tab
2374 execute winnr.'wincmd w'
2375 let restorewinnr = 1
2378 call s:ReloadWinStatus()
2380 if exists('restorewinnr')
2384 execute 'tabnext '.mytab
2388 unlet! t:fugitive_reload_status
2391 function! fugitive#ReloadStatus(...) abort
2392 call s:ExpireStatus(a:0 ? a:1 : -2)
2393 if a:0 > 1 ? a:2 : s:CanAutoReloadStatus()
2395 let t:fugitive_reload_status = t
2396 for tabnr in exists('*settabvar') ? range(1, tabpagenr('$')) : []
2397 call settabvar(tabnr, 'fugitive_reload_status', t)
2399 call s:ReloadTabStatus()
2401 call s:ReloadWinStatus()
2405 function! s:CanAutoReloadStatus() abort
2406 return get(g:, 'fugitive_autoreload_status', !has('win32'))
2409 augroup fugitive_status
2411 autocmd BufWritePost * call fugitive#ReloadStatus(-1, 0)
2412 autocmd ShellCmdPost * nested call fugitive#ReloadStatus()
2413 autocmd BufDelete * nested
2414 \ if getbufvar(+expand('<abuf>'), 'buftype') == 'terminal' |
2415 \ call fugitive#ReloadStatus() |
2418 autocmd FocusGained * call fugitive#ReloadStatus(-2, 0)
2420 autocmd BufEnter index,index.lock
2421 \ call s:ReloadWinStatus()
2423 \ if exists('t:fugitive_reload_status') |
2424 \ call s:ReloadTabStatus() |
2428 function! s:StageInfo(...) abort
2429 let lnum = a:0 ? a:1 : line('.')
2430 let sigil = matchstr(getline(lnum), '^[ @\+-]')
2433 let type = sigil ==# '-' ? '-' : '+'
2434 while lnum > 0 && getline(lnum) !~# '^@'
2435 if getline(lnum) =~# '^[ '.type.']'
2440 let offset += matchstr(getline(lnum), type.'\zs\d\+')
2441 while getline(lnum) =~# '^[ @\+-]'
2445 let slnum = lnum + 1
2448 while len(getline(slnum - 1)) && empty(section)
2450 let section = matchstr(getline(slnum), '^\u\l\+\ze.* (\d\+)$')
2451 if empty(section) && getline(slnum) !~# '^[ @\+-]'
2455 let text = matchstr(getline(lnum), '^[A-Z?] \zs.*')
2456 return {'section': section,
2457 \ 'heading': getline(slnum),
2461 \ 'relative': reverse(split(text, ' -> ')),
2462 \ 'paths': map(reverse(split(text, ' -> ')), 's:Tree() . "/" . v:val'),
2463 \ 'commit': matchstr(getline(lnum), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze '),
2464 \ 'status': matchstr(getline(lnum), '^[A-Z?]\ze \|^\%(\x\x\x\)\@!\l\+\ze [0-9a-f]'),
2468 function! s:Selection(arg1, ...) abort
2470 let arg1 = line('.')
2472 elseif a:arg1 ==# 'v'
2473 let arg1 = line("'<")
2474 let arg2 = line("'>")
2477 let arg2 = a:0 ? a:1 : 0
2481 let last = first - arg2 + 1
2487 while getline(first) =~# '^$\|^[A-Z][a-z]'
2490 if first > last || &filetype !=# 'fugitive'
2494 while getline(flnum) =~# '^[ @\+-]'
2497 let slnum = flnum + 1
2500 while len(getline(slnum - 1)) && empty(section)
2502 let heading = matchstr(getline(slnum), '^\u\l\+.* (\d\+)$')
2503 if empty(heading) && getline(slnum) !~# '^[ @\+-]'
2509 \ 'heading': heading,
2510 \ 'section': matchstr(heading, '^\u\l\+\ze.* (\d\+)$'),
2518 let line = getline(flnum)
2519 let lnum = first - (arg1 == flnum ? 0 : 1)
2520 let root = s:Tree() . '/'
2522 if line =~# '^\u\l\+\ze.* (\d\+)$'
2523 let template.heading = getline(lnum)
2524 let template.section = matchstr(template.heading, '^\u\l\+\ze.* (\d\+)$')
2525 let template.index = 0
2526 elseif line =~# '^[ @\+-]'
2527 let template.index -= 1
2528 if !results[-1].patch
2529 let results[-1].patch = lnum
2531 let results[-1].lnum = lnum
2532 elseif line =~# '^[A-Z?] '
2533 let filename = matchstr(line, '^[A-Z?] \zs.*')
2534 call add(results, extend(deepcopy(template), {
2536 \ 'filename': filename,
2537 \ 'relative': reverse(split(filename, ' -> ')),
2538 \ 'paths': map(reverse(split(filename, ' -> ')), 'root . v:val'),
2539 \ 'status': matchstr(line, '^[A-Z?]'),
2541 elseif line =~# '^\x\x\x\+ '
2542 call add(results, extend({
2544 \ 'commit': matchstr(line, '^\x\x\x\+'),
2545 \ }, template, 'keep'))
2546 elseif line =~# '^\l\+ \x\x\x\+ '
2547 call add(results, extend({
2549 \ 'commit': matchstr(line, '^\l\+ \zs\x\x\x\+'),
2550 \ 'status': matchstr(line, '^\l\+'),
2551 \ }, template, 'keep'))
2554 let template.index += 1
2555 let line = getline(lnum)
2557 if len(results) && results[0].patch && arg2 == 0
2558 while getline(results[0].patch) =~# '^[ \+-]'
2559 let results[0].patch -= 1
2561 while getline(results[0].lnum + 1) =~# '^[ \+-]'
2562 let results[0].lnum += 1
2568 function! s:StageArgs(visual) abort
2571 for record in s:Selection(a:visual ? 'v' : 'n')
2572 if len(record.commit)
2573 call add(commits, record.commit)
2575 call extend(paths, record.paths)
2577 if s:cpath(s:Tree(), getcwd())
2578 call map(paths, 'fugitive#Path(v:val, "./")')
2580 return join(map(commits + paths, 's:fnameescape(v:val)'), ' ')
2583 function! s:Do(action, visual) abort
2584 let line = getline('.')
2586 if !a:0 && !v:count && line =~# '^[A-Z][a-z]'
2587 let header = matchstr(line, '^\S\+\ze:')
2588 if len(header) && exists('*s:Do' . a:action . header . 'Header')
2589 let reload = s:Do{a:action}{header}Header(matchstr(line, ': \zs.*')) > 0
2591 let section = matchstr(line, '^\S\+')
2592 if exists('*s:Do' . a:action . section . 'Heading')
2593 let reload = s:Do{a:action}{section}Heading(line) > 0
2596 return reload ? s:ReloadStatus() : ''
2598 let selection = s:Selection(a:visual ? 'v' : 'n')
2602 call filter(selection, 'v:val.section ==# selection[0].section')
2606 for record in selection
2607 if exists('*s:Do' . a:action . record.section)
2608 let status = s:Do{a:action}{record.section}(record)
2615 let reload = reload || (status > 0)
2618 execute record.lnum + 1
2622 return 'echoerr ' . string(v:exception)
2625 execute s:ReloadStatus()
2627 if exists('success')
2628 call s:StageReveal()
2634 function! s:StageReveal() abort
2636 let begin = line('.')
2637 if getline(begin) =~# '^@'
2639 while getline(end) =~# '^[ \+-]'
2642 elseif getline(begin) =~# '^commit '
2644 while end < line('$') && getline(end + 1) !~# '^commit '
2647 elseif getline(begin) =~# s:section_pattern
2649 while len(getline(end + 1))
2654 while line('.') > line('w0') + &scrolloff && end > line('w$')
2655 execute "normal! \<C-E>"
2660 let s:file_pattern = '^[A-Z?] .\|^diff --'
2661 let s:file_commit_pattern = s:file_pattern . '\|^\%(\l\{3,\} \)\=[0-9a-f]\{4,\} '
2662 let s:item_pattern = s:file_commit_pattern . '\|^@@'
2664 function! s:NextHunk(count) abort
2665 if &filetype ==# 'fugitive' && getline('.') =~# s:file_pattern
2666 exe s:StageInline('show')
2668 for i in range(a:count)
2669 if &filetype ==# 'fugitive'
2670 call search(s:file_pattern . '\|^@', 'W')
2671 if getline('.') =~# s:file_pattern
2672 exe s:StageInline('show')
2673 if getline(line('.') + 1) =~# '^@'
2678 call search('^@@', 'W')
2681 call s:StageReveal()
2685 function! s:PreviousHunk(count) abort
2686 for i in range(a:count)
2687 if &filetype ==# 'fugitive'
2688 let lnum = search(s:file_pattern . '\|^@','Wbn')
2689 call s:StageInline('show', lnum)
2690 call search('^? .\|^@','Wb')
2692 call search('^@@', 'Wb')
2695 call s:StageReveal()
2699 function! s:NextFile(count) abort
2700 for i in range(a:count)
2701 exe s:StageInline('hide')
2702 if !search(s:file_pattern, 'W')
2706 exe s:StageInline('hide')
2710 function! s:PreviousFile(count) abort
2711 exe s:StageInline('hide')
2712 for i in range(a:count)
2713 if !search(s:file_pattern, 'Wb')
2716 exe s:StageInline('hide')
2721 function! s:NextItem(count) abort
2722 for i in range(a:count)
2723 if !search(s:item_pattern, 'W') && getline('.') !~# s:item_pattern
2724 call search('^commit ', 'W')
2727 call s:StageReveal()
2731 function! s:PreviousItem(count) abort
2732 for i in range(a:count)
2733 if !search(s:item_pattern, 'Wbe') && getline('.') !~# s:item_pattern
2734 call search('^commit ', 'Wbe')
2737 call s:StageReveal()
2741 let s:section_pattern = '^[A-Z][a-z][^:]*$'
2742 let s:section_commit_pattern = s:section_pattern . '\|^commit '
2744 function! s:NextSection(count) abort
2745 let orig = line('.')
2746 if getline('.') !~# '^commit '
2749 for i in range(a:count)
2750 if !search(s:section_commit_pattern, 'W')
2754 if getline('.') =~# s:section_commit_pattern
2755 call s:StageReveal()
2756 return getline('.') =~# s:section_pattern ? '+' : ':'
2762 function! s:PreviousSection(count) abort
2763 let orig = line('.')
2764 if getline('.') !~# '^commit '
2767 for i in range(a:count)
2768 if !search(s:section_commit_pattern . '\|\%^', 'bW')
2772 if getline('.') =~# s:section_commit_pattern || line('.') == 1
2773 call s:StageReveal()
2774 return getline('.') =~# s:section_pattern ? '+' : ':'
2780 function! s:NextSectionEnd(count) abort
2782 if empty(getline('.'))
2785 for i in range(a:count)
2786 if !search(s:section_commit_pattern, 'W')
2790 return search('^.', 'Wb')
2793 function! s:PreviousSectionEnd(count) abort
2795 for i in range(a:count)
2796 if search(s:section_commit_pattern, 'Wb') <= 1
2806 return search('^.', 'Wb')
2809 function! s:PatchSearchExpr(reverse) abort
2810 let line = getline('.')
2811 if col('.') ==# 1 && line =~# '^[+-]'
2812 if line =~# '^[+-]\{3\} '
2813 let pattern = '^[+-]\{3\} ' . substitute(escape(strpart(line, 4), '^$.*[]~\'), '^\w/', '\\w/', '') . '$'
2815 let pattern = '^[+-]\s*' . escape(substitute(strpart(line, 1), '^\s*\|\s*$', '', ''), '^$.*[]~\') . '\s*$'
2818 return '?' . escape(pattern, '/') . "\<CR>"
2820 return '/' . escape(pattern, '/?') . "\<CR>"
2823 return a:reverse ? '#' : '*'
2826 function! s:StageInline(mode, ...) abort
2827 if &filetype !=# 'fugitive'
2830 let lnum1 = a:0 ? a:1 : line('.')
2831 let lnum = lnum1 + 1
2832 if a:0 > 1 && a:2 == 0
2833 let info = s:StageInfo(lnum - 1)
2834 if empty(info.paths) && len(info.section)
2835 while len(getline(lnum))
2844 while lnum > 0 && getline(lnum) =~# '^[ @\+-]'
2847 let info = s:StageInfo(lnum)
2848 if !has_key(b:fugitive_diff, info.section)
2851 if getline(lnum + 1) =~# '^[ @\+-]'
2852 let lnum2 = lnum + 1
2853 while getline(lnum2 + 1) =~# '^[ @\+-]'
2856 if a:mode !=# 'show'
2857 setlocal modifiable noreadonly
2858 exe 'silent keepjumps ' . (lnum + 1) . ',' . lnum2 . 'delete _'
2859 call remove(b:fugitive_expanded[info.section], info.filename)
2860 setlocal nomodifiable readonly nomodified
2864 if !has_key(b:fugitive_diff, info.section) || info.status !~# '^[ADMRU]$' || a:mode ==# 'hide'
2871 for line in b:fugitive_diff[info.section]
2872 if mode ==# 'await' && line[0] ==# '@'
2873 let mode = 'capture'
2875 if mode !=# 'head' && line !~# '^[ @\+-]'
2881 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '--- ' . info.relative[-1]
2883 elseif mode ==# 'head' && substitute(line, "\t$", '', '') ==# '+++ ' . info.relative[0]
2885 elseif mode ==# 'capture'
2886 call add(diff, line)
2887 elseif line[0] ==# '@'
2893 setlocal modifiable noreadonly
2894 silent call append(lnum, diff)
2895 let b:fugitive_expanded[info.section][info.filename] = [start, len(diff)]
2896 setlocal nomodifiable readonly nomodified
2902 function! s:NextExpandedHunk(count) abort
2903 for i in range(a:count)
2904 call s:StageInline('show', line('.'), 1)
2905 call search(s:file_pattern . '\|^@','W')
2910 function! s:StageDiff(diff) abort
2911 let lnum = line('.')
2912 let info = s:StageInfo(lnum)
2913 let prefix = info.offset > 0 ? '+' . info.offset : ''
2914 if empty(info.paths) && info.section ==# 'Staged'
2915 return 'Git! diff --no-ext-diff --cached'
2916 elseif empty(info.paths)
2917 return 'Git! diff --no-ext-diff'
2918 elseif len(info.paths) > 1
2919 execute 'Gedit' . prefix s:fnameescape(':0:' . info.paths[0])
2920 return a:diff . '! HEAD:'.s:fnameescape(info.paths[1])
2921 elseif info.section ==# 'Staged' && info.sigil ==# '-'
2922 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2923 return a:diff . '! :0:%'
2924 elseif info.section ==# 'Staged'
2925 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2926 return a:diff . '! @:%'
2927 elseif info.sigil ==# '-'
2928 execute 'Gedit' prefix s:fnameescape(':0:'.info.paths[0])
2929 return a:diff . '! :(top)%'
2931 execute 'Gedit' prefix s:fnameescape(':(top)'.info.paths[0])
2936 function! s:StageDiffEdit() abort
2937 let info = s:StageInfo(line('.'))
2938 let arg = (empty(info.paths) ? s:Tree() : info.paths[0])
2939 if info.section ==# 'Staged'
2940 return 'Git! diff --no-ext-diff --cached '.s:fnameescape(arg)
2941 elseif info.status ==# '?'
2942 call s:TreeChomp('add', '--intent-to-add', '--', arg)
2943 return s:ReloadStatus()
2945 return 'Git! diff --no-ext-diff '.s:fnameescape(arg)
2949 function! s:StageApply(info, reverse, extra) abort
2950 if a:info.status ==# 'R'
2951 call s:throw('fugitive: patching renamed file not yet supported')
2953 let cmd = ['apply', '-p0', '--recount'] + a:extra
2955 let start = info.patch
2957 let lines = getline(start, end)
2958 if empty(filter(copy(lines), 'v:val =~# "^[+-]"'))
2961 while getline(end) =~# '^[-+ ]'
2963 if getline(end) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2964 call add(lines, ' ' . getline(end)[1:-1])
2967 while start > 0 && getline(start) !~# '^@'
2969 if getline(start) =~# '^[' . (a:reverse ? '+' : '-') . ' ]'
2970 call insert(lines, ' ' . getline(start)[1:-1])
2971 elseif getline(start) =~# '^@'
2972 call insert(lines, getline(start))
2976 throw 'fugitive: cold not find hunk'
2977 elseif getline(start) !~# '^@@ '
2978 throw 'fugitive: cannot apply conflict hunk'
2980 let i = b:fugitive_expanded[info.section][info.filename][0]
2982 while get(b:fugitive_diff[info.section], i, '@') !~# '^@'
2983 call add(head, b:fugitive_diff[info.section][i])
2986 call extend(lines, head, 'keep')
2987 let temp = tempname()
2988 call writefile(lines, temp)
2990 call add(cmd, '--reverse')
2992 call extend(cmd, ['--', temp])
2993 let [output, exec_error] = s:ChompError(cmd)
2997 call s:throw(output)
3000 function! s:StageDelete(lnum1, lnum2, count) abort
3004 for info in s:Selection(a:lnum1, a:lnum2)
3005 if empty(info.paths)
3008 let hash = s:TreeChomp('hash-object', '-w', '--', info.paths[0])
3013 call s:StageApply(info, 1, info.section ==# 'Staged' ? ['--index'] : [])
3014 elseif info.status ==# '?'
3015 call s:TreeChomp('clean', '-f', '--', info.paths[0])
3017 call s:TreeChomp('checkout', '--ours', '--', info.paths[0])
3019 call s:TreeChomp('checkout', '--theirs', '--', info.paths[0])
3020 elseif info.status =~# '[ADU]' &&
3021 \ get(b:fugitive_status[info.section ==# 'Staged' ? 'Unstaged' : 'Staged'], info.filename, '') =~# '[AU]'
3022 call s:TreeChomp('checkout', info.section ==# 'Staged' ? '--ours' : '--theirs', '--', info.paths[0])
3023 elseif info.status ==# 'U'
3024 call s:TreeChomp('rm', '--', info.paths[0])
3025 elseif info.status ==# 'A'
3026 call s:TreeChomp('rm', '-f', '--', info.paths[0])
3027 elseif info.section ==# 'Unstaged'
3028 call s:TreeChomp('checkout', '--', info.paths[0])
3030 call s:TreeChomp('checkout', 'HEAD^{}', '--', info.paths[0])
3032 call add(restore, ':Gsplit ' . s:fnameescape(info.relative[0]) . '|Gread ' . hash[0:6])
3035 let err = '|echoerr ' . string(v:exception)
3040 exe s:ReloadStatus()
3041 call s:StageReveal()
3042 return 'checktime|redraw|echomsg ' . string('To restore, ' . join(restore, '|')) . err
3045 function! s:StageIgnore(lnum1, lnum2, count) abort
3047 for info in s:Selection(a:lnum1, a:lnum2)
3048 call extend(paths, info.relative)
3050 call map(paths, '"/" . v:val')
3051 exe 'Gsplit' (a:count ? '.gitignore' : '.git/info/exclude')
3052 let last = line('$')
3053 if last == 1 && empty(getline(1))
3054 call setline(last, paths)
3056 call append(last, paths)
3062 function! s:DoToggleHeadHeader(value) abort
3063 exe 'edit' s:fnameescape(s:Dir())
3064 call search('\C^index$', 'wc')
3067 function! s:DoStageUnpushedHeading(heading) abort
3068 let remote = matchstr(a:heading, 'to \zs[^/]\+\ze/')
3072 let branch = matchstr(a:heading, 'to \%([^/]\+/\)\=\zs\S\+')
3073 call feedkeys(':Gpush ' . remote . ' ' . 'HEAD:' . branch)
3076 function! s:DoToggleUnpushedHeading(heading) abort
3077 return s:DoStageUnpushedHeading(a:heading)
3080 function! s:DoStageUnpushed(record) abort
3081 let remote = matchstr(a:record.heading, 'to \zs[^/]\+\ze/')
3085 let branch = matchstr(a:record.heading, 'to \%([^/]\+/\)\=\zs\S\+')
3086 call feedkeys(':Gpush ' . remote . ' ' . a:record.commit . ':' . branch)
3089 function! s:DoToggleUnpushed(record) abort
3090 return s:DoStageUnpushed(a:record)
3093 function! s:DoUnstageUnpulledHeading(heading) abort
3094 call feedkeys(':Grebase')
3097 function! s:DoToggleUnpulledHeading(heading) abort
3098 call s:DoUnstageUnpulledHeading(a:heading)
3101 function! s:DoUnstageUnpulled(record) abort
3102 call feedkeys(':Grebase ' . a:record.commit)
3105 function! s:DoToggleUnpulled(record) abort
3106 call s:DoUnstageUnpulled(a:record)
3109 function! s:DoUnstageUnpushed(record) abort
3110 call feedkeys(':Grebase --autosquash ' . a:record.commit . '^')
3113 function! s:DoToggleStagedHeading(...) abort
3114 call s:TreeChomp('reset', '-q')
3118 function! s:DoUnstageStagedHeading(heading) abort
3119 return s:DoToggleStagedHeading(a:heading)
3122 function! s:DoToggleUnstagedHeading(...) abort
3123 call s:TreeChomp('add', '-u')
3127 function! s:DoStageUnstagedHeading(heading) abort
3128 return s:DoToggleUnstagedHeading(a:heading)
3131 function! s:DoToggleUntrackedHeading(...) abort
3132 call s:TreeChomp('add', '.')
3136 function! s:DoStageUntrackedHeading(heading) abort
3137 return s:DoToggleUntrackedHeading(a:heading)
3140 function! s:DoToggleStaged(record) abort
3142 return s:StageApply(a:record, 1, ['--cached'])
3144 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3149 function! s:DoUnstageStaged(record) abort
3150 return s:DoToggleStaged(a:record)
3153 function! s:DoToggleUnstaged(record) abort
3154 if a:record.patch && a:record.status !=# 'A'
3155 return s:StageApply(a:record, 0, ['--cached'])
3157 call s:TreeChomp(['add', '-A', '--'] + a:record.paths)
3162 function! s:DoStageUnstaged(record) abort
3163 return s:DoToggleUnstaged(a:record)
3166 function! s:DoUnstageUnstaged(record) abort
3167 if a:record.status ==# 'A'
3168 call s:TreeChomp(['reset', '-q', '--'] + a:record.paths)
3175 function! s:DoToggleUntracked(record) abort
3176 call s:TreeChomp(['add', '--'] + a:record.paths)
3180 function! s:DoStageUntracked(record) abort
3181 return s:DoToggleUntracked(a:record)
3184 function! s:StagePatch(lnum1,lnum2) abort
3189 for lnum in range(a:lnum1,a:lnum2)
3190 let info = s:StageInfo(lnum)
3191 if empty(info.paths) && info.section ==# 'Staged'
3192 return 'Git reset --patch'
3193 elseif empty(info.paths) && info.section ==# 'Unstaged'
3194 return 'Git add --patch'
3195 elseif empty(info.paths) && info.section ==# 'Untracked'
3196 return 'Git add --interactive'
3197 elseif empty(info.paths)
3201 if info.section ==# 'Staged'
3202 let reset += info.relative
3203 elseif info.section ==# 'Untracked'
3204 let intend += info.paths
3205 elseif info.status !~# '^D'
3206 let add += info.relative
3211 call s:TreeChomp(['add', '--intent-to-add', '--'] + intend)
3214 execute "Git add --patch -- ".join(map(add,'s:fnameescape(v:val)'))
3217 execute "Git reset --patch -- ".join(map(reset,'s:fnameescape(v:val)'))
3220 return 'echoerr ' . string(v:exception)
3222 return s:ReloadStatus()
3225 " Section: :Gcommit, :Grevert
3227 function! s:CommitInteractive(line1, line2, range, bang, mods, args, patch) abort
3228 let status = s:StatusCommand(a:line1, a:line2, a:range, a:line2, a:bang, a:mods, '', '', [])
3229 let status = len(status) ? status . '|' : ''
3231 return status . 'if search("^Unstaged")|exe "normal >"|exe "+"|endif'
3233 return status . 'if search("^Untracked\\|^Unstaged")|exe "+"|endif'
3237 function! s:CommitSubcommand(line1, line2, range, bang, mods, args, ...) abort
3238 let mods = substitute(s:Mods(a:mods), '\C\<tab\>', '-tab', 'g')
3239 let dir = a:0 ? a:1 : s:Dir()
3240 let tree = s:Tree(dir)
3241 let msgfile = fugitive#Find('.git/COMMIT_EDITMSG', dir)
3242 let outfile = tempname()
3245 let command = 'set GIT_EDITOR=false& '
3247 let command = 'env GIT_EDITOR=false '
3251 while get(argv, i, '--') !=# '--'
3252 if argv[i] =~# '^-[apzsneiovq].'
3253 call insert(argv, argv[i][0:1])
3254 let argv[i+1] = '-' . argv[i+1][2:-1]
3259 let command .= s:UserCommand(dir, ['commit'] + argv)
3260 if (&autowrite || &autowriteall) && !a:0
3263 if s:HasOpt(argv, '-i', '--interactive')
3264 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 0)
3265 elseif s:HasOpt(argv, '-p', '--patch')
3266 return s:CommitInteractive(a:line1, a:line2, a:range, a:bang, a:mods, argv, 1)
3268 let [error_string, exec_error] = s:TempCmd(outfile, command)
3269 let errors = split(error_string, "\n")
3271 if !has('gui_running')
3275 echo join(errors, "\n")
3276 if filereadable(outfile)
3277 echo join(readfile(outfile), "\n")
3279 call fugitive#ReloadStatus(dir, 1)
3282 let error = get(errors,-2,get(errors,-1,'!'))
3283 if error =~# 'false''\=\.$'
3285 while get(argv, i, '--') !=# '--'
3286 if argv[i] =~# '^\%(-[eips]\|-[CcFm].\+\|--edit\|--interactive\|--patch\|--signoff\|--reedit-message=.*\|--reuse-message=.*\|--file=.*\|--message=.*\)$'
3287 call remove(argv, i)
3288 elseif argv[i] =~# '^\%(-[CcFm]\|--reedit-message\|--reuse-message\|--file\|--message\)$'
3289 call remove(argv, i, i + 1)
3291 if argv[i] =~# '^--cleanup\>'
3297 call insert(argv, '--no-signoff', i)
3298 call insert(argv, '--no-interactive', i)
3299 call insert(argv, '--no-edit', i)
3300 if !exists('cleanup')
3301 call insert(argv, '--cleanup=strip')
3303 call extend(argv, ['-F', msgfile], 'keep')
3304 if (bufname('%') == '' && line('$') == 1 && getline(1) == '' && !&modified) || a:line2 == 0
3305 execute mods . 'keepalt edit' s:fnameescape(msgfile)
3306 elseif s:HasOpt(argv, '-v') || mods =~# '\<tab\>'
3307 execute mods . 'keepalt -tabedit' s:fnameescape(msgfile)
3309 execute mods . 'keepalt split' s:fnameescape(msgfile)
3311 let b:fugitive_commit_arguments = argv
3312 setlocal bufhidden=wipe filetype=gitcommit
3314 elseif empty(errors)
3315 let out = readfile(outfile)
3316 echo get(out, -1, '') =~# 'stash\|\d' ? get(out, -2, '') : get(out, -1, '')
3319 echo join(errors, "\n")
3324 return 'echoerr ' . string(v:exception)
3326 call delete(outfile)
3330 function! s:RevertSubcommand(line1, line2, range, bang, mods, args) abort
3332 let no_commit = s:HasOpt(a:args, '-n', '--no-commit', '--no-edit', '--abort', '--continue', '--quit')
3333 let cmd = s:UserCommand(dir, ['revert'] + (no_commit ? [] : ['-n']) + a:args)
3334 let [out, exec_error] = s:SystemError(cmd)
3335 call fugitive#ReloadStatus(-1, 1)
3336 if no_commit || exec_error
3337 return 'echo ' . string(substitute(out, "\n$", '', ''))
3339 return s:CommitSubcommand(a:line1, a:line2, a:range, a:bang, a:mods, [], dir)
3342 function! fugitive#CommitComplete(A, L, P) abort
3343 if a:A =~# '^--fixup=\|^--squash='
3344 let commits = s:LinesError(['log', '--pretty=format:%s', '@{upstream}..'])[0]
3345 let pre = matchstr(a:A, '^--\w*=''\=') . ':/^'
3347 call map(commits, 'pre . string(tr(v:val, "|\"^$*[]", "......."))[1:-1]')
3348 call filter(commits, 'strpart(v:val, 0, strlen(a:A)) ==# a:A')
3351 return s:FilterEscape(map(commits, 'pre . tr(v:val, "\\ !^$*?[]()''\"`&;<>|#", "....................")'), a:A)
3354 return s:CompleteSub('commit', a:A, a:L, a:P, function('fugitive#CompletePath'))
3359 function! fugitive#RevertComplete(A, L, P) abort
3360 return s:CompleteSub('revert', a:A, a:L, a:P, function('s:CompleteRevision'))
3363 function! s:FinishCommit() abort
3364 let buf = +expand('<abuf>')
3365 let args = getbufvar(buf, 'fugitive_commit_arguments')
3367 call setbufvar(buf, 'fugitive_commit_arguments', [])
3368 if getbufvar(buf, 'fugitive_commit_rebase')
3369 call setbufvar(buf, 'fugitive_commit_rebase', 0)
3370 let s:rebase_continue = s:Dir(buf)
3372 return s:CommitSubcommand(-1, -1, 0, 0, '', args, s:Dir(buf))
3377 call s:command("-nargs=? -range=-1 -complete=customlist,fugitive#CommitComplete Gcommit", "commit")
3378 call s:command("-nargs=? -range=-1 -complete=customlist,fugitive#RevertComplete Grevert", "revert")
3380 " Section: :Gmerge, :Grebase, :Gpull
3382 function! fugitive#MergeComplete(A, L, P) abort
3383 return s:CompleteSub('merge', a:A, a:L, a:P, function('s:CompleteRevision'))
3386 function! fugitive#RebaseComplete(A, L, P) abort
3387 return s:CompleteSub('rebase', a:A, a:L, a:P, function('s:CompleteRevision'))
3390 function! fugitive#PullComplete(A, L, P) abort
3391 return s:CompleteSub('pull', a:A, a:L, a:P, function('s:CompleteRemote'))
3394 function! s:RebaseSequenceAborter() abort
3395 if !exists('s:rebase_sequence_aborter')
3396 let temp = tempname() . '.sh'
3399 \ 'echo exec false | cat - "$1" > "$1.fugitive"',
3400 \ 'mv "$1.fugitive" "$1"'],
3402 let s:rebase_sequence_aborter = temp
3404 return s:rebase_sequence_aborter
3407 function! fugitive#Cwindow() abort
3408 if &buftype == 'quickfix'
3412 if &buftype == 'quickfix'
3418 let s:common_efm = ''
3420 \ . '%+Eusage:%.%#,'
3421 \ . '%+Eerror:%.%#,'
3422 \ . '%+Efatal:%.%#,'
3423 \ . '%-G%.%#%\e[K%.%#,'
3424 \ . '%-G%.%#%\r%.%\+'
3426 let s:rebase_abbrevs = {
3440 function! s:RebaseEdit(cmd, dir) abort
3441 let rebase_todo = s:fnameescape(fugitive#Find('.git/rebase-merge/git-rebase-todo', a:dir))
3443 if filereadable(rebase_todo)
3444 let new = readfile(rebase_todo)
3448 for i in range(len(new))
3449 if new[i] =~# '^\l\+\s\+[0-9a-f]\{5,\}\>'
3450 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3452 let sha_length = len(s:TreeChomp(a:dir, 'rev-parse', '--short', sha))
3454 let shortened_sha = strpart(sha, 0, sha_length)
3455 let shas[shortened_sha] = sha
3456 let new[i] = substitute(new[i], sha, shortened_sha, '')
3459 call writefile(new, rebase_todo)
3461 return a:cmd . ' +setlocal\ bufhidden=wipe\|' . escape('let b:fugitive_rebase_shas = ' . string(shas), ' ') . ' ' . rebase_todo
3464 function! s:MergeRebase(cmd, bang, mods, args, ...) abort
3465 let dir = a:0 ? a:1 : s:Dir()
3467 let mods = s:Mods(a:mods)
3468 if a:cmd =~# '^rebase' && s:HasOpt(args, '-i', '--interactive')
3469 let cmd = fugitive#Prepare(dir, '-c', 'sequence.editor=sh ' . s:RebaseSequenceAborter(), 'rebase') . ' ' . s:shellesc(args)
3470 let out = system(cmd)[0:-2]
3471 for file in ['end', 'msgnum']
3472 let file = fugitive#Find('.git/rebase-merge/' . file, dir)
3473 if !filereadable(file)
3474 return 'echoerr ' . string("fugitive: " . out)
3476 call writefile([readfile(file)[0] - 1], file)
3478 call writefile([], fugitive#Find('.git/rebase-merge/done', dir))
3482 return s:RebaseEdit(mods . 'split', dir)
3483 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--edit-todo') && filereadable(fugitive#Find('.git/rebase-merge/git-rebase-todo', dir))
3484 return s:RebaseEdit(mods . 'split', dir)
3485 elseif a:cmd =~# '^rebase' && s:HasOpt(args, '--continue') && !a:0
3486 let rdir = fugitive#Find('.git/rebase-merge', dir)
3487 let exec_error = s:ChompError([dir, 'diff-index', '--cached', '--quiet', 'HEAD', '--'])[1]
3488 if exec_error && isdirectory(rdir)
3489 if getfsize(rdir . '/amend') <= 0
3490 return 'exe ' . string(mods . 'Gcommit -n -F ' . s:fnameescape(rdir .'/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3491 elseif readfile(rdir . '/amend')[0] ==# fugitive#Head(-1, dir)
3492 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(rdir . '/message') . ' -e') . '|let b:fugitive_commit_rebase = 1'
3496 let had_merge_msg = filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3499 let argv += s:AskPassArgs(dir) + ['pull', '--progress']
3501 call add(argv, a:cmd)
3503 if !s:HasOpt(args, '--no-edit', '--abort', '-m') && a:cmd !=# 'rebase'
3504 call add(argv, '--edit')
3506 if a:cmd ==# 'rebase' && s:HasOpt(args, '--autosquash') && !s:HasOpt(args, '--interactive', '-i')
3507 call add(argv, '--interactive')
3509 call extend(argv, args)
3511 let [mp, efm] = [&l:mp, &l:efm]
3513 let cdback = s:Cd(s:Tree(dir))
3514 let &l:errorformat = ''
3515 \ . '%-Gerror:%.%#false''.,'
3516 \ . '%-G%.%# ''git commit'' %.%#,'
3517 \ . '%+Emerge:%.%#,'
3518 \ . s:common_efm . ','
3519 \ . '%+ECannot %.%#: You have unstaged changes.,'
3520 \ . '%+ECannot %.%#: Your index contains uncommitted changes.,'
3521 \ . '%+EThere is no tracking information for the current branch.,'
3522 \ . '%+EYou are not currently on a branch. Please specify which,'
3523 \ . '%+I %#git rebase --continue,'
3524 \ . 'CONFLICT (%m): %f deleted in %.%#,'
3525 \ . 'CONFLICT (%m): Merge conflict in %f,'
3526 \ . 'CONFLICT (%m): Rename \"%f\"->%.%#,'
3527 \ . 'CONFLICT (%m): Rename %.%#->%f %.%#,'
3528 \ . 'CONFLICT (%m): There is a directory with name %f in %.%#,'
3529 \ . '%+ECONFLICT %.%#,'
3530 \ . '%+EKONFLIKT %.%#,'
3531 \ . '%+ECONFLIT %.%#,'
3532 \ . "%+EXUNG \u0110\u1ed8T %.%#,"
3533 \ . "%+E\u51b2\u7a81 %.%#,"
3535 if a:cmd =~# '^merge' && empty(args) &&
3536 \ (had_merge_msg || isdirectory(fugitive#Find('.git/rebase-apply', dir)) ||
3537 \ !empty(s:TreeChomp(dir, 'diff-files', '--diff-filter=U')))
3538 let cmd = g:fugitive_git_executable.' diff-files --name-status --diff-filter=U'
3540 let cmd = s:UserCommand(dir, argv)
3542 if !empty($GIT_SEQUENCE_EDITOR) || has('win32')
3543 let old_sequence_editor = $GIT_SEQUENCE_EDITOR
3544 let $GIT_SEQUENCE_EDITOR = 'true'
3546 let cmd = 'env GIT_SEQUENCE_EDITOR=true ' . cmd
3548 if !empty($GIT_EDITOR) || has('win32')
3549 let old_editor = $GIT_EDITOR
3550 let $GIT_EDITOR = 'false'
3552 let cmd = 'env GIT_EDITOR=false ' . substitute(cmd, '^env ', '', '')
3554 if !has('patch-8.1.0334') && has('terminal') && &autowrite
3555 let autowrite_was_set = 1
3559 let &l:makeprg = cmd
3560 silent noautocmd make!
3561 catch /^Vim\%((\a\+)\)\=:E211/
3562 let err = v:exception
3564 if exists('autowrite_was_set')
3568 let [&l:mp, &l:efm] = [mp, efm]
3569 if exists('old_editor')
3570 let $GIT_EDITOR = old_editor
3572 if exists('old_sequence_editor')
3573 let $GIT_SEQUENCE_EDITOR = old_sequence_editor
3577 call fugitive#ReloadStatus(dir, 1)
3578 if empty(filter(getqflist(),'v:val.valid && v:val.type !=# "I"'))
3579 if a:cmd =~# '^rebase' &&
3580 \ filereadable(fugitive#Find('.git/rebase-merge/amend', dir)) &&
3581 \ filereadable(fugitive#Find('.git/rebase-merge/done', dir)) &&
3582 \ get(readfile(fugitive#Find('.git/rebase-merge/done', dir)), -1, '') =~# '^[^e]'
3584 return 'exe ' . string(mods . 'Gcommit --amend -n -F ' . s:fnameescape(fugitive#Find('.git/rebase-merge/message', dir)) . ' -e') . '|let b:fugitive_commit_rebase = 1'
3585 elseif !had_merge_msg && filereadable(fugitive#Find('.git/MERGE_MSG', dir))
3587 return mods . 'Gcommit --no-status -n -t '.s:fnameescape(fugitive#Find('.git/MERGE_MSG', dir))
3590 let qflist = getqflist()
3595 let e.pattern = '^<<<<<<<'
3598 call fugitive#Cwindow()
3600 call setqflist(qflist, 'r')
3606 return exists('err') ? 'echoerr '.string(err) : 'exe'
3609 function! s:RebaseClean(file) abort
3610 if !filereadable(a:file)
3613 let old = readfile(a:file)
3615 for i in range(len(new))
3616 let new[i] = substitute(new[i], '^\l\>', '\=get(s:rebase_abbrevs,submatch(0),submatch(0))', '')
3618 let sha = matchstr(new[i], '\C\<[a-f0-9]\{5,\}\>')
3619 let rebase_shas = getbufvar(a:file, 'fugitive_rebase_shas')
3620 if len(sha) && type(rebase_shas) == type({}) && has_key(rebase_shas, sha)
3621 let new[i] = substitute(new[i], '\C\<' . sha . '\>', rebase_shas[sha], '')
3625 call writefile(new, a:file)
3630 function! s:MergeSubcommand(line1, line2, range, bang, mods, args) abort
3631 return s:MergeRebase('merge', a:bang, a:mods, a:args)
3634 function! s:RebaseSubcommand(line1, line2, range, bang, mods, args) abort
3635 return s:MergeRebase('rebase', a:bang, a:mods, a:args)
3638 function! s:PullSubcommand(line1, line2, range, bang, mods, args) abort
3639 return s:MergeRebase('pull', a:bang, a:mods, a:args)
3642 augroup fugitive_merge
3644 autocmd VimLeavePre,BufDelete git-rebase-todo
3645 \ if getbufvar(+expand('<abuf>'), '&bufhidden') ==# 'wipe' |
3646 \ call s:RebaseClean(expand('<afile>')) |
3647 \ if getfsize(FugitiveFind('.git/rebase-merge/done', +expand('<abuf>'))) == 0 |
3648 \ let s:rebase_continue = FugitiveGitDir(+expand('<abuf>')) |
3651 autocmd BufEnter * nested
3652 \ if exists('s:rebase_continue') |
3653 \ exe s:MergeRebase('rebase', 0, '', [getfsize(fugitive#Find('.git/rebase-merge/git-rebase-todo', s:rebase_continue)) > 0 ? '--continue' : '--abort'], remove(s:, 'rebase_continue')) |
3657 call s:command("-nargs=? -bang -complete=customlist,fugitive#MergeComplete Gmerge", "merge")
3658 call s:command("-nargs=? -bang -complete=customlist,fugitive#RebaseComplete Grebase", "rebase")
3659 call s:command("-nargs=? -bang -complete=customlist,fugitive#PullComplete Gpull", "pull")
3661 " Section: :Ggrep, :Glog
3663 if !exists('g:fugitive_summary_format')
3664 let g:fugitive_summary_format = '%s'
3667 function! fugitive#GrepComplete(A, L, P) abort
3668 return s:CompleteSub('grep', a:A, a:L, a:P)
3671 function! fugitive#LogComplete(A, L, P) abort
3672 return s:CompleteSub('log', a:A, a:L, a:P)
3675 function! s:GrepParseLine(prefix, name_only, dir, line) abort
3676 let entry = {'valid': 1}
3677 let match = matchlist(a:line, '^\(.\{-\}\):\(\d\+\):\(\d\+:\)\=\(.*\)$')
3679 let entry.module = match[1]
3680 let entry.lnum = +match[2]
3681 let entry.col = +match[3]
3682 let entry.text = match[4]
3683 elseif a:line =~# '^git: \|^usage: \|^error: \|^fatal: '
3684 return {'text': a:line}
3686 let entry.module = matchstr(a:line, '\CBinary file \zs.*\ze matches$')
3687 if len(entry.module)
3688 let entry.text = 'Binary file'
3692 if empty(entry.module) && a:name_only
3693 let entry.module = a:line
3695 if empty(entry.module)
3696 return {'text': a:line}
3698 if entry.module !~# ':'
3699 let entry.filename = a:prefix . entry.module
3701 let entry.filename = fugitive#Find(entry.module, a:dir)
3706 function! s:GrepSubcommand(line1, line2, range, bang, mods, args) abort
3709 let listnr = a:line1 == 0 ? a:line1 : a:line2
3710 let cmd = ['--no-pager', 'grep', '-n', '--no-color', '--full-name']
3711 if fugitive#GitVersion(2, 19)
3712 call add(cmd, '--column')
3714 let tree = s:Tree(dir)
3715 if type(a:args) == type([])
3716 let [args, after] = [a:args, '']
3718 let [args, after] = s:SplitExpandChain(a:args, tree)
3720 let prefix = FugitiveVimPath(s:HasOpt(args, '--cached') || empty(tree) ? 'fugitive://' . dir . '//0/' : tree . '/')
3721 let name_only = s:HasOpt(args, '-l', '--files-with-matches', '--name-only', '-L', '--files-without-match')
3722 let title = [listnr < 0 ? ':Ggrep' : ':Glgrep'] + args
3724 exe listnr 'wincmd w'
3729 call s:QuickfixCreate(listnr, {'title': (listnr < 0 ? ':Ggrep ' : ':Glgrep ') . s:fnameescape(args)})
3730 let tempfile = tempname()
3731 if v:version >= 704 | exe 'silent doautocmd <nomodeline> QuickFixCmdPre ' (listnr < 0 ? 'Ggrep' : 'Glgrep') | endif
3732 exe '!' . escape(s:UserCommand(dir, cmd + args), '%#!')
3733 \ printf(&shellpipe . (&shellpipe =~# '%s' ? '' : ' %s'), s:shellesc(tempfile))
3734 let list = map(readfile(tempfile), 's:GrepParseLine(prefix, name_only, dir, v:val)')
3735 call s:QuickfixSet(listnr, list, 'a')
3736 if v:version >= 704 | exe 'silent doautocmd <nomodeline> QuickFixCmdPost ' (listnr < 0 ? 'Ggrep' : 'Glgrep') | endif
3737 if !has('gui_running')
3740 if !a:bang && !empty(list)
3741 return (listnr < 0 ? 'c' : 'l').'first' . after
3747 function! s:LogFlushQueue(state) abort
3748 let queue = remove(a:state, 'queue')
3749 if a:state.child_found
3750 call remove(queue, 0)
3752 if len(queue) && queue[-1] ==# {'text': ''}
3753 call remove(queue, -1)
3758 function! s:LogParse(state, dir, line) abort
3759 if a:state.context ==# 'hunk' && a:line =~# '^[-+ ]'
3762 let list = matchlist(a:line, '^\%(fugitive \(.\{-\}\)\t\|commit \|From \)\=\(\x\{40,\}\)\%( \(.*\)\)\=$')
3764 let a:state.context = 'commit'
3765 let a:state.base = 'fugitive://' . a:dir . '//' . list[2]
3766 let a:state.base_module = len(list[1]) ? list[1] : list[2]
3767 let a:state.message = list[3]
3768 if has_key(a:state, 'diffing')
3769 call remove(a:state, 'diffing')
3771 let queue = s:LogFlushQueue(a:state)
3772 let a:state.queue = [{
3774 \ 'filename': a:state.base . a:state.target,
3775 \ 'module': a:state.base_module . substitute(a:state.target, '^/', ':', ''),
3776 \ 'text': a:state.message}]
3777 let a:state.child_found = 0
3779 elseif type(a:line) == type(0)
3780 return s:LogFlushQueue(a:state)
3781 elseif a:line =~# '^diff'
3782 let a:state.context = 'diffhead'
3783 elseif a:line =~# '^[+-]\{3\} \w/' && a:state.context ==# 'diffhead'
3784 let a:state.diffing = a:line[5:-1]
3785 elseif a:line =~# '^@@[^@]*+\d' && has_key(a:state, 'diffing') && has_key(a:state, 'base')
3786 let a:state.context = 'hunk'
3787 if empty(a:state.target) || a:state.target ==# a:state.diffing
3788 let a:state.child_found = 1
3789 call add(a:state.queue, {
3791 \ 'filename': a:state.base . a:state.diffing,
3792 \ 'module': a:state.base_module . substitute(a:state.diffing, '^/', ':', ''),
3793 \ 'lnum': +matchstr(a:line, '+\zs\d\+'),
3794 \ 'text': a:state.message . matchstr(a:line, ' @@\+ .\+')})
3796 elseif a:state.follow &&
3797 \ a:line =~# '^ \%(mode change \d\|\%(create\|delete\) mode \d\|\%(rename\|copy\|rewrite\) .* (\d\+%)$\)'
3798 let rename = matchstr(a:line, '^ rename \zs.* => .*\ze (\d\+%)$')
3800 let rename = rename =~# '{.* => .*}' ? rename : '{' . rename . '}'
3801 if a:state.target ==# simplify('/' . substitute(rename, '{.* => \(.*\)}', '\1', ''))
3802 let a:state.target = simplify('/' . substitute(rename, '{\(.*\) => .*}', '\1', ''))
3805 if !get(a:state, 'ignore_summary')
3806 call add(a:state.queue, {'text': a:line})
3808 elseif a:state.context ==# 'commit' || a:state.context ==# 'init'
3809 call add(a:state.queue, {'text': a:line})
3814 function! fugitive#LogCommand(line1, count, range, bang, mods, args, type) abort
3817 let listnr = a:type =~# '^l' ? 0 : -1
3818 let [args, after] = s:SplitExpandChain(a:args, s:Tree(dir))
3819 let split = index(args, '--')
3821 let paths = args[split : -1]
3822 let args = args[0 : split - 1]
3829 if a:line1 == 0 && a:count
3830 let path = fugitive#Path(bufname(a:count), '/', dir)
3832 let path = fugitive#Path(@%, '/', dir)
3838 let state = {'context': 'init', 'child_found': 0, 'queue': [], 'follow': 0}
3839 if path =~# '^/\.git\%(/\|$\)\|^$'
3842 let range = "0," . (a:count ? a:count : bufnr(''))
3843 let extra = ['.' . path]
3844 if (empty(paths) || paths ==# ['--']) && !s:HasOpt(args, '--no-follow')
3845 let state.follow = 1
3846 if !s:HasOpt(args, '--follow')
3847 call insert(args, '--follow')
3849 if !s:HasOpt(args, '--summary')
3850 call insert(args, '--summary')
3851 let state.ignore_summary = 1
3855 if !s:HasOpt(args, '--merges', '--no-merges')
3856 call insert(args, '--no-merges')
3858 call add(args, '-L' . a:line1 . ',' . a:count . ':' . path[1:-1])
3860 if len(path) && empty(filter(copy(args), 'v:val =~# "^[^-]"'))
3861 let owner = s:Owner(@%, dir)
3863 call add(args, owner)
3869 if s:HasOpt(args, '-g', '--walk-reflogs')
3870 let format = "%gd\t%H %gs"
3872 let format = "%h\t%H " . g:fugitive_summary_format
3874 let cmd = ['--no-pager']
3875 if fugitive#GitVersion(1, 9)
3876 call extend(cmd, ['-c', 'diff.context=0', '-c', 'diff.noprefix=false', 'log'])
3878 call extend(cmd, ['log', '-U0', '--no-patch'])
3881 \ ['--no-color', '--no-ext-diff', '--pretty=format:fugitive ' . format] +
3882 \ args + paths + extra)
3883 let state.target = path
3884 let title = (listnr < 0 ? ':Gclog ' : ':Gllog ') . s:fnameescape(args + paths)
3885 if empty(paths + extra) && empty(a:type) && len(s:Relative('/'))
3886 let after = '|echohl WarningMsg|echo ' . string('Use :0Glog or :0Gclog for old behavior of targeting current file') . '|echohl NONE' . after
3888 return s:QuickfixStream(listnr, title, s:UserCommandList(dir) + cmd, !a:bang, s:function('s:LogParse'), state, dir) . after
3891 " Section: :Gedit, :Gpedit, :Gsplit, :Gvsplit, :Gtabedit, :Gread
3893 function! s:UsableWin(nr) abort
3894 return a:nr && !getwinvar(a:nr, '&previewwindow') && !getwinvar(a:nr, '&winfixwidth') &&
3895 \ (empty(getwinvar(a:nr, 'fugitive_status')) || getbufvar(winbufnr(a:nr), 'fugitive_type') !=# 'index') &&
3896 \ index(['gitrebase', 'gitcommit'], getbufvar(winbufnr(a:nr), '&filetype')) < 0 &&
3897 \ index(['nofile','help','quickfix'], getbufvar(winbufnr(a:nr), '&buftype')) < 0
3900 function! s:OpenParse(args) abort
3902 let args = copy(a:args)
3903 while !empty(args) && args[0] =~# '^+'
3904 call add(pre, ' ' . escape(remove(args, 0), ' |"'))
3907 let file = join(args)
3908 elseif empty(expand('%'))
3910 elseif empty(s:DirCommitFile(@%)[1]) && s:Relative('./') !~# '^\./\.git\>'
3915 return [s:Expand(file), join(pre)]
3918 function! s:DiffClose() abort
3919 let mywinnr = winnr()
3920 for winnr in [winnr('#')] + range(winnr('$'),1,-1)
3921 if winnr != mywinnr && getwinvar(winnr,'&diff')
3922 execute winnr.'wincmd w'
3932 function! s:BlurStatus() abort
3933 if (&previewwindow || exists('w:fugitive_status')) && get(b:,'fugitive_type', '') ==# 'index'
3934 let winnrs = filter([winnr('#')] + range(1, winnr('$')), 's:UsableWin(v:val)')
3936 exe winnrs[0].'wincmd w'
3946 function! s:OpenExec(cmd, mods, args, ...) abort
3947 let dir = a:0 ? s:Dir(a:1) : s:Dir()
3948 let temp = tempname()
3949 let columns = get(g:, 'fugitive_columns', 80)
3953 let env = 'set COLUMNS=' . columns . '& '
3955 let env = 'env COLUMNS=' . columns . ' '
3957 silent! execute '!' . escape(env . s:UserCommand(dir, ['--no-pager'] + a:args), '!#%') .
3958 \ (&shell =~# 'csh' ? ' >& ' . temp : ' > ' . temp . ' 2>&1')
3960 let temp = s:Resolve(temp)
3961 let first = join(readfile(temp, '', 2), "\n")
3962 if first =~# '\<\([[:upper:][:digit:]_-]\+(\d\+)\).*\1'
3963 let filetype = 'man'
3965 let filetype = 'git'
3967 let s:temp_files[s:cpath(temp)] = { 'dir': dir, 'filetype': filetype, 'modifiable': first =~# '^diff ' }
3971 silent execute s:Mods(a:mods) . a:cmd temp
3972 call fugitive#ReloadStatus(dir, 1)
3973 return 'echo ' . string(':!' . s:UserCommand(dir, a:args))
3976 function! fugitive#Open(cmd, bang, mods, arg, args) abort
3978 return s:OpenExec(a:cmd, a:mods, s:SplitExpand(a:arg, s:Tree()))
3981 let mods = s:Mods(a:mods)
3983 let [file, pre] = s:OpenParse(a:args)
3984 let file = s:Generate(file)
3986 return 'echoerr ' . string(v:exception)
3988 if file !~# '^\a\a\+:'
3989 let file = s:sub(file, '/$', '')
3994 return mods . a:cmd . pre . ' ' . s:fnameescape(file)
3997 function! fugitive#ReadCommand(line1, count, range, bang, mods, arg, args) abort
3998 let mods = s:Mods(a:mods)
4001 let delete = 'silent 1,' . line('$') . 'delete_|'
4002 let after = line('$')
4004 let delete = 'silent ' . a:line1 . ',' . a:count . 'delete_|'
4010 let args = s:SplitExpand(a:arg, s:Tree(dir))
4011 silent execute mods . after . 'read!' escape(s:UserCommand(dir, ['--no-pager'] + args), '!#%')
4012 execute delete . 'diffupdate'
4013 call fugitive#ReloadStatus()
4014 return 'redraw|echo '.string(':!'.s:UserCommand(dir, args))
4017 let [file, pre] = s:OpenParse(a:args)
4018 let file = s:Generate(file)
4020 return 'echoerr ' . string(v:exception)
4022 if file =~# '^fugitive:' && after is# 0
4023 return 'exe ' .string(mods . fugitive#FileReadCmd(file, 0, pre)) . '|diffupdate'
4026 exe after . 'foldopen!'
4028 return mods . after . 'read' . pre . ' ' . s:fnameescape(file) . '|' . delete . 'diffupdate' . (a:count < 0 ? '|' . line('.') : '')
4031 function! fugitive#ReadComplete(A, L, P) abort
4033 return fugitive#Complete(a:A, a:L, a:P)
4035 return fugitive#CompleteObject(a:A, a:L, a:P)
4039 " Section: :Gwrite, :Gwq
4041 function! fugitive#WriteCommand(line1, line2, range, bang, mods, arg, args) abort
4042 if exists('b:fugitive_commit_arguments')
4043 return 'write|bdelete'
4044 elseif expand('%:t') == 'COMMIT_EDITMSG' && $GIT_INDEX_FILE != ''
4046 elseif get(b:, 'fugitive_type', '') ==# 'index'
4048 elseif &buftype ==# 'nowrite' && getline(4) =~# '^+++ '
4049 let filename = getline(4)[6:-1]
4052 setlocal buftype=nowrite
4053 if matchstr(getline(2),'index [[:xdigit:]]\+\.\.\zs[[:xdigit:]]\{7\}') ==# fugitive#RevParse(':0:'.filename)[0:6]
4054 let [message, exec_error] = s:ChompError(['apply', '--cached', '--reverse', '--', expand('%:p')])
4056 let [message, exec_error] = s:ChompError(['apply', '--cached', '--', expand('%:p')])
4066 return 'Gedit '.fnameescape(filename)
4069 let mytab = tabpagenr()
4070 let mybufnr = bufnr('')
4072 let file = len(a:args) ? s:Generate(s:Expand(join(a:args, ' '))) : fugitive#Real(@%)
4074 return 'echoerr ' . string(v:exception)
4077 return 'echoerr '.string('fugitive: cannot determine file path')
4079 if file =~# '^fugitive:'
4080 return 'write' . (a:bang ? '! ' : ' ') . s:fnameescape(file)
4083 let always_permitted = s:cpath(fugitive#Real(@%), file) && empty(s:DirCommitFile(@%)[1])
4084 if !always_permitted && !a:bang && (len(s:TreeChomp('diff', '--name-status', 'HEAD', '--', file)) || len(s:TreeChomp('ls-files', '--others', '--', file)))
4085 let v:errmsg = 'fugitive: file has uncommitted changes (use ! to override)'
4086 return 'echoerr v:errmsg'
4089 for nr in range(1,bufnr('$'))
4090 if fnamemodify(bufname(nr),':p') ==# file
4095 if treebufnr > 0 && treebufnr != bufnr('')
4096 let temp = tempname()
4097 silent execute 'keepalt %write '.temp
4098 for tab in [mytab] + range(1,tabpagenr('$'))
4099 for winnr in range(1,tabpagewinnr(tab,'$'))
4100 if tabpagebuflist(tab)[winnr-1] == treebufnr
4101 execute 'tabnext '.tab
4103 execute winnr.'wincmd w'
4104 let restorewinnr = 1
4107 let lnum = line('.')
4108 let last = line('$')
4109 silent execute '$read '.temp
4110 silent execute '1,'.last.'delete_'
4116 if exists('restorewinnr')
4119 execute 'tabnext '.mytab
4126 call writefile(readfile(temp,'b'),file,'b')
4129 execute 'write! '.s:fnameescape(file)
4133 let [error, exec_error] = s:ChompError(['add', '--force', '--', file])
4135 let [error, exec_error] = s:ChompError(['add', '--', file])
4138 let v:errmsg = 'fugitive: '.error
4139 return 'echoerr v:errmsg'
4141 if s:cpath(fugitive#Real(@%), file) && s:DirCommitFile(@%)[1] =~# '^\d$'
4145 let one = s:Generate(':1:'.file)
4146 let two = s:Generate(':2:'.file)
4147 let three = s:Generate(':3:'.file)
4148 for nr in range(1,bufnr('$'))
4149 let name = fnamemodify(bufname(nr), ':p')
4150 if bufloaded(nr) && !getbufvar(nr,'&modified') && (name ==# one || name ==# two || name ==# three)
4151 execute nr.'bdelete'
4156 let zero = s:Generate(':0:'.file)
4157 silent execute 'doautocmd' s:nomodeline 'BufWritePost' s:fnameescape(zero)
4158 for tab in range(1,tabpagenr('$'))
4159 for winnr in range(1,tabpagewinnr(tab,'$'))
4160 let bufnr = tabpagebuflist(tab)[winnr-1]
4161 let bufname = fnamemodify(bufname(bufnr), ':p')
4162 if bufname ==# zero && bufnr != mybufnr
4163 execute 'tabnext '.tab
4165 execute winnr.'wincmd w'
4166 let restorewinnr = 1
4169 let lnum = line('.')
4170 let last = line('$')
4171 silent execute '$read '.s:fnameescape(file)
4172 silent execute '1,'.last.'delete_'
4177 if exists('restorewinnr')
4180 execute 'tabnext '.mytab
4186 call fugitive#ReloadStatus()
4190 function! fugitive#WqCommand(...) abort
4191 let bang = a:4 ? '!' : ''
4192 if exists('b:fugitive_commit_arguments')
4195 let result = call('fugitive#WriteCommand', a:000)
4196 if result =~# '^\%(write\|wq\|echoerr\)'
4197 return s:sub(result,'^write','wq')
4199 return result.'|quit'.bang
4203 augroup fugitive_commit
4205 autocmd VimLeavePre,BufDelete COMMIT_EDITMSG execute substitute(s:FinishCommit(), '\C^echoerr \(''[^'']*''\)*', 'redraw|echohl ErrorMsg|echo \1|echohl NONE', '')
4208 " Section: :Gpush, :Gfetch
4210 function! fugitive#PushComplete(A, L, P) abort
4211 return s:CompleteSub('push', a:A, a:L, a:P, function('s:CompleteRemote'))
4214 function! fugitive#FetchComplete(A, L, P) abort
4215 return s:CompleteSub('fetch', a:A, a:L, a:P, function('s:CompleteRemote'))
4218 function! s:AskPassArgs(dir) abort
4219 if (len($DISPLAY) || len($TERM_PROGRAM) || has('gui_running')) && fugitive#GitVersion(1, 8) &&
4220 \ empty($GIT_ASKPASS) && empty($SSH_ASKPASS) && empty(fugitive#Config('core.askPass', a:dir))
4221 if s:executable(s:ExecPath() . '/git-gui--askpass')
4222 return ['-c', 'core.askPass=' . s:ExecPath() . '/git-gui--askpass']
4223 elseif s:executable('ssh-askpass')
4224 return ['-c', 'core.askPass=ssh-askpass']
4230 function! s:Dispatch(bang, cmd, args) abort
4232 let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
4234 let b:current_compiler = 'git'
4235 let &l:errorformat = s:common_efm
4236 let &l:makeprg = s:UserCommand(dir, s:AskPassArgs(dir) + [a:cmd] + a:args)
4237 if exists(':Make') == 2
4241 if !has('patch-8.1.0334') && has('terminal') && &autowrite
4242 let autowrite_was_set = 1
4246 silent noautocmd make!
4248 return 'call fugitive#Cwindow()|silent doautocmd ShellCmdPost'
4251 let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
4252 if empty(cc) | unlet! b:current_compiler | endif
4253 if exists('autowrite_was_set')
4259 function! s:PushSubcommand(line1, line2, range, bang, mods, args) abort
4260 return s:Dispatch(a:bang ? '!' : '', 'push', a:args)
4263 function! s:FetchSubcommand(line1, line2, range, bang, mods, args) abort
4264 return s:Dispatch(a:bang ? '!' : '', 'fetch', a:args)
4267 call s:command("-nargs=? -bang -complete=customlist,fugitive#PushComplete Gpush", "push")
4268 call s:command("-nargs=? -bang -complete=customlist,fugitive#FetchComplete Gfetch", "fetch")
4272 augroup fugitive_diff
4274 autocmd BufWinLeave *
4275 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 2 |
4276 \ call s:diffoff_all(s:Dir(+expand('<abuf>'))) |
4278 autocmd BufWinEnter *
4279 \ if s:can_diffoff(+expand('<abuf>')) && s:diff_window_count() == 1 |
4280 \ call s:diffoff() |
4284 function! s:can_diffoff(buf) abort
4285 return getwinvar(bufwinnr(a:buf), '&diff') &&
4286 \ !empty(getwinvar(bufwinnr(a:buf), 'fugitive_diff_restore'))
4289 function! fugitive#CanDiffoff(buf) abort
4290 return s:can_diffoff(bufnr(a:buf))
4293 function! s:diff_modifier(count) abort
4294 let fdc = matchstr(&diffopt, 'foldcolumn:\zs\d\+')
4295 if &diffopt =~# 'horizontal' && &diffopt !~# 'vertical'
4297 elseif &diffopt =~# 'vertical'
4299 elseif winwidth(0) <= a:count * ((&tw ? &tw : 80) + (empty(fdc) ? 2 : fdc))
4306 function! s:diff_window_count() abort
4308 for nr in range(1,winnr('$'))
4309 let c += getwinvar(nr,'&diff')
4314 function! s:diff_restore() abort
4315 let restore = 'setlocal nodiff noscrollbind'
4316 \ . ' scrollopt=' . &l:scrollopt
4317 \ . (&l:wrap ? ' wrap' : ' nowrap')
4318 \ . ' foldlevel=999'
4319 \ . ' foldmethod=' . &l:foldmethod
4320 \ . ' foldcolumn=' . &l:foldcolumn
4321 \ . ' foldlevel=' . &l:foldlevel
4322 \ . (&l:foldenable ? ' foldenable' : ' nofoldenable')
4323 if has('cursorbind')
4324 let restore .= (&l:cursorbind ? ' ' : ' no') . 'cursorbind'
4329 function! s:diffthis() abort
4331 let w:fugitive_diff_restore = s:diff_restore()
4336 function! s:diffoff() abort
4337 if exists('w:fugitive_diff_restore')
4338 execute w:fugitive_diff_restore
4339 unlet w:fugitive_diff_restore
4345 function! s:diffoff_all(dir) abort
4346 let curwin = winnr()
4347 for nr in range(1,winnr('$'))
4348 if getwinvar(nr,'&diff')
4350 execute nr.'wincmd w'
4351 let restorewinnr = 1
4353 if s:Dir() ==# a:dir
4358 execute curwin.'wincmd w'
4361 function! s:CompareAge(mine, theirs) abort
4362 let scores = {':0': 1, ':1': 2, ':2': 3, ':': 4, ':3': 5}
4363 let mine = substitute(a:mine, '^:', '', '')
4364 let theirs = substitute(a:theirs, '^:', '', '')
4365 let my_score = get(scores, ':'.mine, 0)
4366 let their_score = get(scores, ':'.theirs, 0)
4367 if my_score || their_score
4368 return my_score < their_score ? -1 : my_score != their_score
4369 elseif mine ==# theirs
4372 let base = s:TreeChomp('merge-base', mine, theirs)
4375 elseif base ==# theirs
4378 let my_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:mine, '--')
4379 let their_time = +s:TreeChomp('log', '--max-count=1', '--pretty=format:%at', a:theirs, '--')
4380 return my_time < their_time ? -1 : my_time != their_time
4383 function! s:IsConflicted() abort
4384 return len(@%) && !empty(s:ChompDefault('', 'ls-files', '--unmerged', '--', expand('%:p')))
4387 function! fugitive#Diffsplit(autodir, keepfocus, mods, arg, args) abort
4388 let args = copy(a:args)
4390 if get(args, 0) =~# '^+'
4391 let post = remove(args, 0)[1:-1]
4393 if exists(':DiffGitCached') && empty(args)
4394 return s:Mods(a:mods) . 'DiffGitCached' . (len(post) ? '|' . post : '')
4396 let commit = s:DirCommitFile(@%)[1]
4397 if a:mods =~# '\<tab\>'
4398 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4399 let pre = 'tab split'
4401 let mods = 'keepalt ' . a:mods
4404 let back = exists('*win_getid') ? 'call win_gotoid(' . win_getid() . ')' : 'wincmd p'
4405 if (empty(args) || args[0] ==# ':') && a:keepfocus
4407 if empty(commit) && s:IsConflicted()
4408 let parents = [s:Relative(':2:'), s:Relative(':3:')]
4409 elseif empty(commit)
4410 let parents = [s:Relative(':0:')]
4411 elseif commit =~# '^\d\=$'
4412 let parents = [s:Relative('HEAD:')]
4413 elseif commit =~# '^\x\x\+$'
4414 let parents = s:LinesError(['rev-parse', commit . '^@'])[0]
4415 call map(parents, 's:Relative(v:val . ":")')
4419 if exists('parents') && len(parents) > 1
4421 let mods = (a:autodir ? s:diff_modifier(len(parents) + 1) : '') . s:Mods(mods, 'leftabove')
4423 execute mods 'split' s:fnameescape(s:Generate(parents[0]))
4424 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4428 call s:Map('n', 'd2o', ':diffget '.nr2.'<Bar>diffupdate<CR>', '<silent>')
4429 let mods = substitute(mods, '\Cleftabove\|rightbelow\|aboveleft\|belowright', '\=submatch(0) =~# "f" ? "rightbelow" : "leftabove"', '')
4430 for i in range(len(parents)-1, 1, -1)
4431 execute mods 'split' s:fnameescape(s:Generate(parents[i]))
4432 call s:Map('n', 'dp', ':diffput '.nr.'<Bar>diffupdate<CR>', '<silent>')
4436 call s:Map('n', 'd' . (i + 2) . 'o', ':diffget '.nrx.'<Bar>diffupdate<CR>', '<silent>')
4444 let arg = join(args, ' ')
4449 let file = s:Relative()
4452 let file = s:Relative(':0:')
4453 elseif arg =~# '^:\d$'
4455 let file = s:Relative(arg . ':')
4458 let file = arg =~# '^:/.' ? fugitive#RevParse(arg) . s:Relative(':') : s:Expand(arg)
4460 return 'echoerr ' . string(v:exception)
4463 elseif exists('parents') && len(parents)
4464 let file = parents[-1]
4466 let file = s:Relative()
4467 elseif s:IsConflicted()
4468 let file = s:Relative(':1:')
4469 let post = 'echohl WarningMsg|echo "Use :Gdiffsplit! for 3 way diff"|echohl NONE|' . post
4472 let file = s:Relative(':0:')
4474 let spec = s:Generate(file)
4475 if spec =~# '^fugitive:' && empty(s:DirCommitFile(spec)[2])
4476 let spec = FugitiveVimPath(spec . s:Relative('/'))
4479 let restore = s:diff_restore()
4480 let w:fugitive_diff_restore = restore
4481 if s:CompareAge(commit, s:DirCommitFile(spec)[1]) < 0
4482 let mods = s:Mods(mods, 'rightbelow')
4484 let mods = s:Mods(mods, 'leftabove')
4486 let mods = (a:autodir ? s:diff_modifier(2) : '') . mods
4487 if &diffopt =~# 'vertical'
4488 let diffopt = &diffopt
4489 set diffopt-=vertical
4491 execute mods 'diffsplit' s:fnameescape(spec)
4492 let &l:readonly = &l:readonly
4494 let w:fugitive_diff_restore = restore
4496 if getwinvar('#', '&diff')
4503 return 'echoerr ' . string(v:exception)
4505 if exists('diffopt')
4506 let &diffopt = diffopt
4511 " Section: :Gmove, :Gremove
4513 function! s:Move(force, rename, destination) abort
4516 if s:DirCommitFile(@%)[1] !~# '^0\=$' || empty(@%)
4517 return 'echoerr ' . string('fugitive: mv not supported for this buffer')
4519 if a:destination =~# '^\.\.\=\%(/\|$\)'
4520 let destination = simplify(getcwd() . '/' . a:destination)
4521 elseif a:destination =~# '^\a\+:\|^/'
4522 let destination = a:destination
4523 elseif a:destination =~# '^:/:\='
4524 let destination = s:Tree(dir) . substitute(a:destination, '^:/:\=', '', '')
4525 elseif a:destination =~# '^:(\%(top\|top,literal\|literal,top\))'
4526 let destination = s:Tree(dir) . matchstr(a:destination, ')\zs.*')
4527 elseif a:destination =~# '^:(literal)'
4528 let destination = simplify(getcwd() . '/' . matchstr(a:destination, ')\zs.*'))
4530 let destination = expand('%:p:s?[\/]$??:h') . '/' . a:destination
4532 let destination = s:Tree(dir) . '/' . a:destination
4534 let destination = s:Slash(destination)
4538 let [message, exec_error] = s:ChompError(['mv'] + (a:force ? ['-f'] : []) + ['--', expand('%:p'), destination], dir)
4540 let v:errmsg = 'fugitive: '.message
4541 return 'echoerr v:errmsg'
4543 if isdirectory(destination)
4544 let destination = fnamemodify(s:sub(destination,'/$','').'/'.expand('%:t'),':.')
4546 call fugitive#ReloadStatus(dir)
4547 if empty(s:DirCommitFile(@%)[1])
4548 if isdirectory(destination)
4549 return 'keepalt edit '.s:fnameescape(destination)
4551 return 'keepalt saveas! '.s:fnameescape(destination)
4554 return 'file '.s:fnameescape(fugitive#Find(':0:'.destination, dir))
4558 function! fugitive#RenameComplete(A,L,P) abort
4559 if a:A =~# '^[.:]\=/'
4560 return fugitive#CompletePath(a:A)
4562 let pre = s:Slash(fnamemodify(expand('%:p:s?[\/]$??'), ':h')) . '/'
4563 return map(fugitive#CompletePath(pre.a:A), 'strpart(v:val, len(pre))')
4567 function! fugitive#MoveCommand(line1, line2, range, bang, mods, arg, args) abort
4568 return s:Move(a:bang, 0, a:arg)
4571 function! fugitive#RenameCommand(line1, line2, range, bang, mods, arg, args) abort
4572 return s:Move(a:bang, 1, a:arg)
4575 function! s:Remove(after, force) abort
4578 if len(@%) && s:DirCommitFile(@%)[1] ==# ''
4580 elseif s:DirCommitFile(@%)[1] ==# '0'
4581 let cmd = ['rm','--cached']
4583 return 'echoerr ' . string('fugitive: rm not supported for this buffer')
4586 let cmd += ['--force']
4588 let [message, exec_error] = s:ChompError(cmd + ['--', expand('%:p')], dir)
4590 let v:errmsg = 'fugitive: '.s:sub(message,'error:.*\zs\n\(.*-f.*',' (add ! to force)')
4591 return 'echoerr '.string(v:errmsg)
4593 call fugitive#ReloadStatus(dir)
4594 return a:after . (a:force ? '!' : '')
4598 function! fugitive#RemoveCommand(line1, line2, range, bang, mods, arg, args) abort
4599 return s:Remove('edit', a:bang)
4602 function! fugitive#DeleteCommand(line1, line2, range, bang, mods, arg, args) abort
4603 return s:Remove('bdelete', a:bang)
4608 function! s:Keywordprg() abort
4609 let args = ' --git-dir='.escape(s:Dir(),"\\\"' ")
4610 if has('gui_running') && !has('win32')
4611 return s:UserCommand() . ' --no-pager' . args . ' log -1'
4613 return s:UserCommand() . args . ' show'
4617 function! s:linechars(pattern) abort
4618 let chars = strlen(s:gsub(matchstr(getline('.'), a:pattern), '.', '.'))
4619 if exists('*synconcealed') && &conceallevel > 1
4620 for col in range(1, chars)
4621 let chars -= synconcealed(line('.'), col)[0]
4627 function! s:BlameBufnr(...) abort
4628 let state = s:TempState(bufname(a:0 ? a:1 : ''))
4629 if get(state, 'filetype', '') ==# 'fugitiveblame'
4630 return get(state, 'bufnr', -1)
4636 function! s:BlameCommitFileLnum(...) abort
4637 let line = a:0 ? a:1 : getline('.')
4638 let state = a:0 ? a:2 : s:TempState()
4639 let commit = matchstr(line, '^\^\=\zs\x\+')
4640 if commit =~# '^0\+$'
4642 elseif line !~# '^\^' && has_key(state, 'blame_reverse_end')
4643 let commit = get(s:LinesError('rev-list', '--ancestry-path', '--reverse', commit . '..' . state.blame_reverse_end)[0], 0, '')
4645 let lnum = +matchstr(line, ' \zs\d\+\ze \%((\| *\d\+)\)')
4646 let path = matchstr(line, '^\^\=[?*]*\x* \+\%(\d\+ \+\d\+ \+\)\=\zs.\{-\}\ze\s\+\%(\%( \d\+ \)\@<!([^()]*\w \d\+)\|\d\+ \)')
4647 if empty(path) && lnum
4648 let path = get(state, 'blame_file', '')
4650 return [commit, path, lnum]
4653 function! s:BlameLeave() abort
4654 let bufwinnr = bufwinnr(s:BlameBufnr())
4656 let bufnr = bufnr('')
4657 exe bufwinnr . 'wincmd w'
4658 return bufnr . 'bdelete'
4663 function! s:BlameQuit() abort
4664 let cmd = s:BlameLeave()
4667 elseif len(s:DirCommitFile(@%)[1])
4668 return cmd . '|Gedit'
4674 function! fugitive#BlameComplete(A, L, P) abort
4675 return s:CompleteSub('blame', a:A, a:L, a:P)
4678 function! s:BlameSubcommand(line1, count, range, bang, mods, args) abort
4680 let flags = copy(a:args)
4686 if a:line1 > 0 && a:count > 0 && a:range != 1
4687 call extend(ranges, ['-L', a:line1 . ',' . a:count])
4689 while i < len(flags)
4690 let match = matchlist(flags[i], '^\(-[a-zABDFH-KN-RT-Z]\)\ze\(.*\)')
4691 if len(match) && len(match[2])
4692 call insert(flags, match[1])
4693 let flags[i+1] = '-' . match[2]
4697 if arg =~# '^-p$\|^--\%(help\|porcelain\|line-porcelain\|incremental\)$'
4699 elseif arg ==# '--contents' && i + 1 < len(flags)
4700 call extend(commits, remove(flags, i, i+1))
4702 elseif arg ==# '-L' && i + 1 < len(flags)
4703 call extend(ranges, remove(flags, i, i+1))
4705 elseif arg =~# '^--contents='
4706 call add(commits, remove(flags, i))
4708 elseif arg =~# '^-L.'
4709 call add(ranges, remove(flags, i))
4711 elseif arg =~# '^-[GLS]$\|^--\%(date\|encoding\|contents\|ignore-rev\|ignore-revs-file\)$'
4715 echo s:ChompError(['blame', arg])[0]
4720 if i + 1 < len(flags)
4721 call extend(files, remove(flags, i + 1, -1))
4723 call remove(flags, i)
4725 elseif arg !~# '^-' && (s:HasOpt(flags, '--not') || arg !~# '^\^')
4726 if index(flags, '--') >= 0
4727 call add(commits, remove(flags, i))
4730 if arg =~# '\.\.' && arg !~# '^\.\.\=\%(/\|$\)' && empty(commits)
4731 call add(commits, remove(flags, i))
4735 let dcf = s:DirCommitFile(fugitive#Find(arg))
4736 if len(dcf[1]) && empty(dcf[2])
4737 call add(commits, remove(flags, i))
4742 call add(files, remove(flags, i))
4747 let file = substitute(get(files, 0, get(s:TempState(), 'blame_file', s:Relative('./'))), '^\.\%(/\|$\)', '', '')
4748 if empty(commits) && len(files) > 1
4749 call add(commits, remove(files, 1))
4753 let cmd = ['--no-pager', '-c', 'blame.coloring=none', '-c', 'blame.blankBoundary=false', 'blame', '--show-number']
4754 call extend(cmd, filter(copy(flags), 'v:val !~# "\\v^%(-b|--%(no-)=color-.*|--progress)$"'))
4755 if a:count > 0 && empty(ranges)
4756 let cmd += ['-L', (a:line1 ? a:line1 : line('.')) . ',' . (a:line1 ? a:line1 : line('.'))]
4758 call extend(cmd, ranges)
4761 elseif empty(files) && len(matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$'))
4762 let cmd += [matchstr(s:DirCommitFile(@%)[1], '^\x\x\+$')]
4763 elseif empty(files) && !s:HasOpt(flags, '--reverse')
4764 let cmd += ['--contents', '-']
4766 let basecmd = escape(fugitive#Prepare(cmd) . ' -- ' . s:shellesc(len(files) ? files : file), '!#%')
4767 let tempname = tempname()
4768 let error = tempname . '.err'
4769 let temp = tempname . (raw ? '' : '.fugitiveblame')
4771 silent! execute '%write !('.basecmd.' > '.temp.') >& '.error
4773 silent! execute '%write !'.basecmd.' > '.temp.' 2> '.error
4778 let lines = readfile(error)
4780 let lines = readfile(temp)
4782 for i in range(len(lines))
4783 if lines[i] =~# '^error: \|^fatal: '
4791 if i != len(lines) - 1
4797 let temp_state = {'dir': s:Dir(), 'filetype': (raw ? '' : 'fugitiveblame'), 'blame_flags': flags, 'blame_file': file, 'modifiable': 0}
4798 if s:HasOpt(flags, '--reverse')
4799 let temp_state.blame_reverse_end = matchstr(get(commits, 0, ''), '\.\.\zs.*')
4801 if (a:line1 == 0 || a:range == 1) && a:count > 0
4802 let edit = s:Mods(a:mods) . get(['edit', 'split', 'pedit', 'vsplit', 'tabedit'], a:count - (a:line1 ? a:line1 : 1), 'split')
4803 return s:BlameCommit(edit, get(readfile(temp), 0, ''), temp_state)
4805 let temp = s:Resolve(temp)
4806 let s:temp_files[s:cpath(temp)] = temp_state
4807 if len(ranges + commits + files) || raw
4808 let mods = s:Mods(a:mods)
4810 exe 'silent keepalt' mods 'split' s:fnameescape(temp)
4811 elseif !&modified || a:bang || &bufhidden ==# 'hide' || (empty(&bufhidden) && &hidden)
4812 exe 'silent' mods 'edit' . (a:bang ? '! ' : ' ') . s:fnameescape(temp)
4814 return mods . 'edit ' . s:fnameescape(temp)
4818 if a:mods =~# '\<tab\>'
4821 let mods = substitute(a:mods, '\<tab\>', '', 'g')
4822 for winnr in range(winnr('$'),1,-1)
4823 if getwinvar(winnr, '&scrollbind')
4824 call setwinvar(winnr, '&scrollbind', 0)
4826 if exists('+cursorbind') && getwinvar(winnr, '&cursorbind')
4827 call setwinvar(winnr, '&cursorbind', 0)
4829 if s:BlameBufnr(winbufnr(winnr)) > 0
4830 execute winbufnr(winnr).'bdelete'
4833 let bufnr = bufnr('')
4834 let temp_state.bufnr = bufnr
4835 let restore = 'call setwinvar(bufwinnr('.bufnr.'),"&scrollbind",0)'
4836 if exists('+cursorbind')
4837 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&cursorbind",0)'
4840 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&wrap",1)'
4843 let restore .= '|call setwinvar(bufwinnr('.bufnr.'),"&foldenable",1)'
4845 setlocal scrollbind nowrap nofoldenable
4846 if exists('+cursorbind')
4849 let top = line('w0') + &scrolloff
4850 let current = line('.')
4851 exe 'silent keepalt' (a:bang ? s:Mods(mods) . 'split' : s:Mods(mods, 'leftabove') . 'vsplit') s:fnameescape(temp)
4852 let w:fugitive_leave = restore
4856 if exists('+cursorbind')
4859 setlocal nonumber scrollbind nowrap foldcolumn=0 nofoldenable winfixwidth
4860 if exists('+relativenumber')
4861 setlocal norelativenumber
4863 execute "vertical resize ".(s:linechars('.\{-\}\ze\s\+\d\+)')+1)
4864 call s:Map('n', 'A', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze [0-9:/+-][0-9:/+ -]* \\d\\+)')+1+v:count)<CR>", '<silent>')
4865 call s:Map('n', 'C', ":<C-u>exe 'vertical resize '.(<SID>linechars('^\\S\\+')+1+v:count)<CR>", '<silent>')
4866 call s:Map('n', 'D', ":<C-u>exe 'vertical resize '.(<SID>linechars('.\\{-\\}\\ze\\d\\ze\\s\\+\\d\\+)')+1-v:count)<CR>", '<silent>')
4873 return 'echoerr ' . string(v:exception)
4877 function! s:BlameCommit(cmd, ...) abort
4878 let line = a:0 ? a:1 : getline('.')
4879 let state = a:0 ? a:2 : s:TempState()
4880 let sigil = has_key(state, 'blame_reverse_end') ? '-' : '+'
4881 let mods = (s:BlameBufnr() < 0 ? '' : &splitbelow ? "botright " : "topleft ")
4882 let [commit, path, lnum] = s:BlameCommitFileLnum(line, state)
4883 if empty(commit) && len(path) && has_key(state, 'blame_reverse_end')
4884 let path = (len(state.blame_reverse_end) ? state.blame_reverse_end . ':' : ':(top)') . path
4885 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4887 if commit =~# '^0*$'
4888 return 'echoerr ' . string('fugitive: no commit')
4890 if line =~# '^\^' && !has_key(state, 'blame_reverse_end')
4891 let path = commit . ':' . path
4892 return fugitive#Open(mods . a:cmd, 0, '', '+' . lnum . ' ' . s:fnameescape(path), ['+' . lnum, path])
4894 let cmd = fugitive#Open(mods . a:cmd, 0, '', commit, [commit])
4895 if cmd =~# '^echoerr'
4899 if a:cmd ==# 'pedit' || empty(path)
4902 if search('^diff .* b/\M'.escape(path,'\').'$','W')
4904 let head = line('.')
4905 while search('^@@ \|^diff ') && getline('.') =~# '^@@ '
4906 let top = +matchstr(getline('.'),' ' . sigil .'\zs\d\+')
4907 let len = +matchstr(getline('.'),' ' . sigil . '\d\+,\zs\d\+')
4908 if lnum >= top && lnum <= top + len
4909 let offset = lnum - top
4917 while offset > 0 && line('.') < line('$')
4919 if getline('.') =~# '^[ ' . sigil . ']'
4932 function! s:BlameJump(suffix, ...) abort
4933 let suffix = a:suffix
4934 let [commit, path, lnum] = s:BlameCommitFileLnum()
4936 return 'echoerr ' . string('fugitive: could not determine filename for blame')
4938 if commit =~# '^0*$'
4942 let offset = line('.') - line('w0')
4943 let flags = get(s:TempState(), 'blame_flags', [])
4945 if s:HasOpt(flags, '--reverse')
4946 call remove(flags, '--reverse')
4948 call add(flags, '--reverse')
4951 let blame_bufnr = s:BlameBufnr()
4953 let bufnr = bufnr('')
4954 let winnr = bufwinnr(blame_bufnr)
4956 exe winnr.'wincmd w'
4958 execute 'Gedit' s:fnameescape(commit . suffix . ':' . path)
4964 if exists(':Gblame')
4965 let my_bufnr = bufnr('')
4967 let blame_args = flags + [commit . suffix, '--', path]
4968 let result = s:BlameSubcommand(0, 0, 0, 0, '', blame_args)
4970 let blame_args = flags
4971 let result = s:BlameSubcommand(-1, -1, 0, 0, '', blame_args)
4973 if bufnr('') == my_bufnr
4978 let delta = line('.') - line('w0') - offset
4980 execute 'normal! '.delta."\<C-E>"
4982 execute 'normal! '.(-delta)."\<C-Y>"
4986 echo ':Gblame' s:fnameescape(blame_args)
4991 let s:hash_colors = {}
4993 function! fugitive#BlameSyntax() abort
4994 let conceal = has('conceal') ? ' conceal' : ''
4995 let config = fugitive#Config()
4996 let flags = get(s:TempState(), 'blame_flags', [])
4997 syn match FugitiveblameBlank "^\s\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalFile,FugitiveblameOriginalLineNumber skipwhite
4998 syn match FugitiveblameHash "\%(^\^\=[?*]*\)\@<=\<\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
4999 syn match FugitiveblameUncommitted "\%(^\^\=\)\@<=\<0\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5000 if get(get(config, 'blame.blankboundary', ['x']), 0, 'x') =~# '^$\|^true$' || s:HasOpt(flags, '-b')
5001 syn match FugitiveblameBoundaryIgnore "^\^[*?]*\x\{7,\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameScoreDebug,FugitiveblameOriginalLineNumber,FugitiveblameOriginalFile skipwhite
5003 syn match FugitiveblameBoundary "^\^"
5005 syn match FugitiveblameScoreDebug " *\d\+\s\+\d\+\s\@=" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile contained skipwhite
5006 syn region FugitiveblameAnnotation matchgroup=FugitiveblameDelimiter start="(" end="\%(\s\d\+\)\@<=)" contained keepend oneline
5007 syn match FugitiveblameTime "[0-9:/+-][0-9:/+ -]*[0-9:/+-]\%(\s\+\d\+)\)\@=" contained containedin=FugitiveblameAnnotation
5008 exec 'syn match FugitiveblameLineNumber "\s*\d\+)\@=" contained containedin=FugitiveblameAnnotation' conceal
5009 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)
5010 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s(\)\@=" contained nextgroup=FugitiveblameAnnotation skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5011 exec 'syn match FugitiveblameOriginalLineNumber "\s*\d\+\%(\s\+\d\+)\)\@=" contained nextgroup=FugitiveblameShort skipwhite' (s:HasOpt(flags, '--show-number', '-n') ? '' : conceal)
5012 syn match FugitiveblameShort " \d\+)" contained contains=FugitiveblameLineNumber
5013 syn match FugitiveblameNotCommittedYet "(\@<=Not Committed Yet\>" contained containedin=FugitiveblameAnnotation
5014 hi def link FugitiveblameBoundary Keyword
5015 hi def link FugitiveblameHash Identifier
5016 hi def link FugitiveblameBoundaryIgnore Ignore
5017 hi def link FugitiveblameUncommitted Ignore
5018 hi def link FugitiveblameScoreDebug Debug
5019 hi def link FugitiveblameTime PreProc
5020 hi def link FugitiveblameLineNumber Number
5021 hi def link FugitiveblameOriginalFile String
5022 hi def link FugitiveblameOriginalLineNumber Float
5023 hi def link FugitiveblameShort FugitiveblameDelimiter
5024 hi def link FugitiveblameDelimiter Delimiter
5025 hi def link FugitiveblameNotCommittedYet Comment
5026 if !get(g:, 'fugitive_dynamic_colors', 1) && !s:HasOpt(flags, '--color-lines') || s:HasOpt(flags, '--no-color-lines')
5030 for lnum in range(1, line('$'))
5031 let hash = matchstr(getline(lnum), '^\^\=\zs\x\{6\}')
5032 if hash ==# '' || hash ==# '000000' || has_key(seen, hash)
5036 if &t_Co > 16 && get(g:, 'CSApprox_loaded') && !empty(findfile('autoload/csapprox/per_component.vim', escape(&rtp, ' ')))
5037 \ && empty(get(s:hash_colors, hash))
5038 let [s, r, g, b; __] = map(matchlist(hash, '\(\x\x\)\(\x\x\)\(\x\x\)'), 'str2nr(v:val,16)')
5039 let color = csapprox#per_component#Approximate(r, g, b)
5040 if color == 16 && &background ==# 'dark'
5043 let s:hash_colors[hash] = ' ctermfg='.color
5045 let s:hash_colors[hash] = ''
5047 exe 'syn match FugitiveblameHash'.hash.' "\%(^\^\=\)\@<='.hash.'\x\{1,34\}\>" nextgroup=FugitiveblameAnnotation,FugitiveblameOriginalLineNumber,fugitiveblameOriginalFile skipwhite'
5049 call s:BlameRehighlight()
5052 function! s:BlameRehighlight() abort
5053 for [hash, cterm] in items(s:hash_colors)
5054 if !empty(cterm) || has('gui_running') || has('termguicolors') && &termguicolors
5055 exe 'hi FugitiveblameHash'.hash.' guifg=#'.hash.get(s:hash_colors, hash, '')
5057 exe 'hi link FugitiveblameHash'.hash.' Identifier'
5062 function! s:BlameFileType() abort
5064 setlocal foldmethod=manual
5066 let &l:keywordprg = s:Keywordprg()
5068 let b:undo_ftplugin = 'setl keywordprg= foldmethod<'
5069 if exists('+concealcursor')
5070 setlocal concealcursor=nc conceallevel=2
5071 let b:undo_ftplugin .= ' concealcursor< conceallevel<'
5076 call s:Map('n', '<F1>', ':help fugitive-:Gblame<CR>', '<silent>')
5077 call s:Map('n', 'g?', ':help fugitive-:Gblame<CR>', '<silent>')
5078 if mapcheck('q', 'n') =~# '^$\|bdelete'
5079 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>')
5081 call s:Map('n', 'gq', ':exe <SID>BlameQuit()<CR>', '<silent>')
5082 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5083 call s:Map('n', '<CR>', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5084 call s:Map('n', '-', ':<C-U>exe <SID>BlameJump("")<CR>', '<silent>')
5085 call s:Map('n', 'P', ':<C-U>exe <SID>BlameJump("^".v:count1)<CR>', '<silent>')
5086 call s:Map('n', '~', ':<C-U>exe <SID>BlameJump("~".v:count1)<CR>', '<silent>')
5087 call s:Map('n', 'i', ':<C-U>exe <SID>BlameCommit("exe <SID>BlameLeave()<Bar>edit")<CR>', '<silent>')
5088 call s:Map('n', 'o', ':<C-U>exe <SID>BlameCommit("split")<CR>', '<silent>')
5089 call s:Map('n', 'O', ':<C-U>exe <SID>BlameCommit("tabedit")<CR>', '<silent>')
5090 call s:Map('n', 'p', ':<C-U>exe <SID>BlameCommit("pedit")<CR>', '<silent>')
5093 augroup fugitive_blame
5095 autocmd FileType fugitiveblame call s:BlameFileType()
5096 autocmd ColorScheme,GUIEnter * call s:BlameRehighlight()
5097 autocmd BufWinLeave * execute getwinvar(+bufwinnr(+expand('<abuf>')), 'fugitive_leave')
5100 call s:command('-buffer -bang -range=-1 -nargs=? -complete=customlist,fugitive#BlameComplete Gblame', 'blame')
5104 let s:redirects = {}
5106 function! fugitive#BrowseCommand(line1, count, range, bang, mods, arg, args) abort
5110 let validremote = '\.\|\.\=/.*\|[[:alnum:]_-]\+\%(://.\{-\}\)\='
5113 return 'echoerr ' . string('fugitive: ''-'' no longer required to get persistent URL if range given')
5115 return 'echoerr ' . string('fugitive: use :0Gbrowse instead of :Gbrowse -')
5118 let remote = matchstr(join(a:args, ' '),'@\zs\%('.validremote.'\)$')
5119 let rev = substitute(join(a:args, ' '),'@\%('.validremote.'\)$','','')
5125 let rev = s:DirRev(@%)[1]
5128 let expanded = s:Relative()
5130 let expanded = s:Expand(rev)
5132 let cdir = FugitiveVimPath(fugitive#CommonDir(dir))
5133 for subdir in ['tags/', 'heads/', 'remotes/']
5134 if expanded !~# '^[./]' && filereadable(cdir . '/refs/' . subdir . expanded)
5135 let expanded = '.git/refs/' . subdir . expanded
5138 let full = fugitive#Find(expanded, dir)
5140 if full =~? '^fugitive:'
5141 let [pathdir, commit, path] = s:DirCommitFile(full)
5142 if commit =~# '^:\=\d$'
5146 let type = s:TreeChomp('cat-file','-t',commit.s:sub(path,'^/',':'))
5147 let branch = matchstr(expanded, '^[^:]*')
5151 let path = path[1:-1]
5152 elseif empty(s:Tree(dir))
5153 let path = '.git/' . full[strlen(dir)+1:-1]
5156 let path = fugitive#Path(full, '/')[1:-1]
5157 if path =~# '^\.git/'
5159 elseif isdirectory(full) || empty(path)
5165 if type ==# 'tree' && !empty(path)
5166 let path = s:sub(path, '/\=$', '/')
5168 if path =~# '^\.git/.*HEAD$' && filereadable(dir . '/' . path[5:-1])
5169 let body = readfile(dir . '/' . path[5:-1])[0]
5170 if body =~# '^\x\{40,\}$'
5174 elseif body =~# '^ref: refs/'
5175 let path = '.git/' . matchstr(body,'ref: \zs.*')
5180 if path =~# '^\.git/refs/remotes/.'
5182 let remote = matchstr(path, '^\.git/refs/remotes/\zs[^/]\+')
5183 let branch = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5185 let merge = matchstr(path, '^\.git/refs/remotes/[^/]\+/\zs.\+')
5186 let path = '.git/refs/heads/'.merge
5188 elseif path =~# '^\.git/refs/heads/.'
5189 let branch = path[16:-1]
5190 elseif !exists('branch')
5191 let branch = FugitiveHead()
5194 let r = fugitive#Config('branch.'.branch.'.remote')
5195 let m = fugitive#Config('branch.'.branch.'.merge')[11:-1]
5196 if r ==# '.' && !empty(m)
5197 let r2 = fugitive#Config('branch.'.m.'.remote')
5200 let m = fugitive#Config('branch.'.m.'.merge')[11:-1]
5206 if r ==# '.' || r ==# remote
5208 if path =~# '^\.git/refs/heads/.'
5209 let path = '.git/refs/heads/'.merge
5214 let line1 = a:count > 0 ? a:line1 : 0
5215 let line2 = a:count > 0 ? a:count : 0
5216 if empty(commit) && path !~# '^\.git/'
5217 if a:count < 0 && !empty(merge)
5222 let owner = s:Owner(@%)
5223 let [commit, exec_error] = s:ChompError(['merge-base', 'refs/remotes/' . remote . '/' . merge, empty(owner) ? 'HEAD' : owner, '--'])
5227 if a:count > 0 && empty(a:args) && commit =~# '^\x\{40,\}$'
5228 let blame_list = tempname()
5229 call writefile([commit, ''], blame_list, 'b')
5230 let blame_in = tempname()
5231 silent exe '%write' blame_in
5232 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])
5234 let blame_regex = '^\^\x\+\s\+\zs\d\+\ze\s'
5235 if get(blame, 0) =~# blame_regex && get(blame, -1) =~# blame_regex
5236 let line1 = +matchstr(blame[0], blame_regex)
5237 let line2 = +matchstr(blame[-1], blame_regex)
5239 call s:throw("Can't browse to uncommitted change")
5246 let commit = readfile(fugitive#Find('.git/HEAD', dir), '', 1)[0]
5249 while commit =~# '^ref: ' && i < 10
5250 let commit = readfile(cdir . '/' . commit[5:-1], '', 1)[0]
5258 let raw = fugitive#RemoteUrl(remote)
5263 if raw =~# '^https\=://' && s:executable('curl')
5264 if !has_key(s:redirects, raw)
5265 let s:redirects[raw] = matchstr(system('curl -I ' .
5266 \ s:shellesc(raw . '/info/refs?service=git-upload-pack')),
5267 \ 'Location: \zs\S\+\ze/info/refs?')
5269 if len(s:redirects[raw])
5270 let raw = s:redirects[raw]
5276 \ 'repo': fugitive#repo(dir),
5278 \ 'revision': 'No longer provided',
5286 for Handler in get(g:, 'fugitive_browse_handlers', [])
5287 let url = call(Handler, [copy(opts)])
5294 call s:throw("No Gbrowse handler installed for '".raw."'")
5297 let url = s:gsub(url, '[ <>]', '\="%".printf("%02X",char2nr(submatch(0)))')
5302 return 'echomsg '.string(url)
5303 elseif exists(':Browse') == 2
5304 return 'echomsg '.string(url).'|Browse '.url
5306 if !exists('g:loaded_netrw')
5307 runtime! autoload/netrw.vim
5309 if exists('*netrw#BrowseX')
5310 return 'echomsg '.string(url).'|call netrw#BrowseX('.string(url).', 0)'
5312 return 'echomsg '.string(url).'|call netrw#NetrwBrowseX('.string(url).', 0)'
5316 return 'echoerr ' . string(v:exception)
5320 " Section: Go to file
5322 nnoremap <SID>: :<C-U><C-R>=v:count ? v:count : ''<CR>
5323 function! fugitive#MapCfile(...) abort
5324 exe 'cnoremap <buffer> <expr> <Plug><cfile>' (a:0 ? a:1 : 'fugitive#Cfile()')
5325 let b:undo_ftplugin = get(b:, 'undo_ftplugin', 'exe') . '|sil! exe "cunmap <buffer> <Plug><cfile>"'
5326 if !exists('g:fugitive_no_maps')
5327 call s:Map('n', 'gf', '<SID>:find <Plug><cfile><CR>', '<silent><unique>', 1)
5328 call s:Map('n', '<C-W>f', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5329 call s:Map('n', '<C-W><C-F>', '<SID>:sfind <Plug><cfile><CR>', '<silent><unique>', 1)
5330 call s:Map('n', '<C-W>gf', '<SID>:tabfind <Plug><cfile><CR>', '<silent><unique>', 1)
5331 call s:Map('c', '<C-R><C-F>', '<Plug><cfile>', '<silent><unique>', 1)
5335 function! s:ContainingCommit() abort
5336 let commit = s:Owner(@%)
5337 return empty(commit) ? 'HEAD' : commit
5340 function! s:SquashArgument(...) abort
5341 if &filetype == 'fugitive'
5342 let commit = matchstr(getline('.'), '^\%(\%(\x\x\x\)\@!\l\+\s\+\)\=\zs[0-9a-f]\{4,\}\ze ')
5343 elseif has_key(s:temp_files, s:cpath(expand('%:p')))
5344 let commit = matchstr(getline('.'), '\<\x\{4,\}\>')
5346 let commit = s:Owner(@%)
5348 return len(commit) && a:0 ? printf(a:1, commit) : commit
5351 function! s:RebaseArgument() abort
5352 return s:SquashArgument(' %s^')
5355 function! s:NavigateUp(count) abort
5356 let rev = substitute(s:DirRev(@%)[1], '^$', ':', 'g')
5360 let rev = matchstr(rev, '.*\ze/.\+', '')
5361 elseif rev =~# '.:.'
5362 let rev = matchstr(rev, '^.[^:]*:')
5375 function! s:MapMotion(lhs, rhs) abort
5376 call s:Map('n', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5377 call s:Map('o', a:lhs, ":<C-U>" . a:rhs . "<CR>", "<silent>")
5378 call s:Map('x', a:lhs, ":<C-U>exe 'normal! gv'<Bar>" . a:rhs . "<CR>", "<silent>")
5381 function! fugitive#MapJumps(...) abort
5383 if get(b:, 'fugitive_type', '') ==# 'blob'
5384 let blame_map = 'Gblame<C-R>=v:count ? " --reverse" : ""<CR><CR>'
5385 call s:Map('n', '<2-LeftMouse>', ':<C-U>0,1' . blame_map, '<silent>')
5386 call s:Map('n', '<CR>', ':<C-U>0,1' . blame_map, '<silent>')
5387 call s:Map('n', 'o', ':<C-U>0,2' . blame_map, '<silent>')
5388 call s:Map('n', 'p', ':<C-U>0,3' . blame_map, '<silent>')
5389 call s:Map('n', 'gO', ':<C-U>0,4' . blame_map, '<silent>')
5390 call s:Map('n', 'O', ':<C-U>0,5' . blame_map, '<silent>')
5392 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>')
5393 call s:Map('n', 'dd', ":<C-U>call <SID>DiffClose()<Bar>Gdiffsplit!<CR>", '<silent>')
5394 call s:Map('n', 'dh', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5395 call s:Map('n', 'ds', ":<C-U>call <SID>DiffClose()<Bar>Ghdiffsplit!<CR>", '<silent>')
5396 call s:Map('n', 'dv', ":<C-U>call <SID>DiffClose()<Bar>Gvdiffsplit!<CR>", '<silent>')
5397 call s:Map('n', 'd?', ":<C-U>help fugitive_d<CR>", '<silent>')
5400 call s:Map('n', '<2-LeftMouse>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5401 call s:Map('n', '<CR>', ':<C-U>exe <SID>GF("edit")<CR>', '<silent>')
5402 call s:Map('n', 'o', ':<C-U>exe <SID>GF("split")<CR>', '<silent>')
5403 call s:Map('n', 'gO', ':<C-U>exe <SID>GF("vsplit")<CR>', '<silent>')
5404 call s:Map('n', 'O', ':<C-U>exe <SID>GF("tabedit")<CR>', '<silent>')
5405 call s:Map('n', 'p', ':<C-U>exe <SID>GF("pedit")<CR>', '<silent>')
5407 if !exists('g:fugitive_no_maps')
5408 if exists(':CtrlP') && get(g:, 'ctrl_p_map') =~? '^<c-p>$'
5409 nnoremap <buffer> <silent> <C-P> :<C-U>execute line('.') == 1 ? 'CtrlP ' . fnameescape(<SID>Tree()) : <SID>PreviousItem(v:count1)<CR>
5411 nnoremap <buffer> <silent> <C-P> :<C-U>execute <SID>PreviousItem(v:count1)<CR>
5413 nnoremap <buffer> <silent> <C-N> :<C-U>execute <SID>NextItem(v:count1)<CR>
5415 call s:MapMotion('(', 'exe <SID>PreviousItem(v:count1)')
5416 call s:MapMotion(')', 'exe <SID>NextItem(v:count1)')
5417 call s:MapMotion('K', 'exe <SID>PreviousHunk(v:count1)')
5418 call s:MapMotion('J', 'exe <SID>NextHunk(v:count1)')
5419 call s:MapMotion('[c', 'exe <SID>PreviousHunk(v:count1)')
5420 call s:MapMotion(']c', 'exe <SID>NextHunk(v:count1)')
5421 call s:MapMotion('[/', 'exe <SID>PreviousFile(v:count1)')
5422 call s:MapMotion(']/', 'exe <SID>NextFile(v:count1)')
5423 call s:MapMotion('[m', 'exe <SID>PreviousFile(v:count1)')
5424 call s:MapMotion(']m', 'exe <SID>NextFile(v:count1)')
5425 call s:MapMotion('[[', 'exe <SID>PreviousSection(v:count1)')
5426 call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
5427 call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
5428 call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
5429 call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
5430 call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
5432 call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent>')
5433 call s:Map('n', 'dq', ":<C-U>call <SID>DiffClose()<CR>", '<silent>')
5434 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>')
5435 call s:Map('n', 'P', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'^'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5436 call s:Map('n', '~', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit().'~'.v:count1.<SID>Relative(':'))<CR>", '<silent>')
5437 call s:Map('n', 'C', ":<C-U>exe 'Gedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5438 call s:Map('n', 'cp', ":<C-U>echoerr 'Use gC'<CR>", '<silent>')
5439 call s:Map('n', 'gC', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5440 call s:Map('n', 'gc', ":<C-U>exe 'Gpedit ' . <SID>fnameescape(<SID>ContainingCommit())<CR>", '<silent>')
5441 call s:Map('n', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5442 call s:Map('x', 'gi', ":<C-U>exe 'Gsplit' (v:count ? '.gitignore' : '.git/info/exclude')<CR>", '<silent>')
5444 nnoremap <buffer> c<Space> :Git commit<Space>
5445 nnoremap <buffer> c<CR> :Git commit<CR>
5446 nnoremap <buffer> cv<Space> :Git commit -v<Space>
5447 nnoremap <buffer> cv<CR> :Git commit -v<CR>
5448 nnoremap <buffer> <silent> ca :<C-U>Gcommit --amend<CR>
5449 nnoremap <buffer> <silent> cc :<C-U>Gcommit<CR>
5450 nnoremap <buffer> <silent> ce :<C-U>Gcommit --amend --no-edit<CR>
5451 nnoremap <buffer> <silent> cw :<C-U>Gcommit --amend --only<CR>
5452 nnoremap <buffer> <silent> cva :<C-U>Gcommit -v --amend<CR>
5453 nnoremap <buffer> <silent> cvc :<C-U>Gcommit -v<CR>
5454 nnoremap <buffer> <silent> cRa :<C-U>Gcommit --reset-author --amend<CR>
5455 nnoremap <buffer> <silent> cRe :<C-U>Gcommit --reset-author --amend --no-edit<CR>
5456 nnoremap <buffer> <silent> cRw :<C-U>Gcommit --reset-author --amend --only<CR>
5457 nnoremap <buffer> cf :<C-U>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5458 nnoremap <buffer> cF :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --fixup=<C-R>=<SID>SquashArgument()<CR>
5459 nnoremap <buffer> cs :<C-U>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5460 nnoremap <buffer> cS :<C-U><Bar>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><Home>Gcommit --squash=<C-R>=<SID>SquashArgument()<CR>
5461 nnoremap <buffer> cA :<C-U>Gcommit --edit --squash=<C-R>=<SID>SquashArgument()<CR>
5462 nnoremap <buffer> <silent> c? :<C-U>help fugitive_c<CR>
5464 nnoremap <buffer> cr<Space> :Git revert<Space>
5465 nnoremap <buffer> cr<CR> :Git revert<CR>
5466 nnoremap <buffer> <silent> crc :<C-U>Grevert <C-R>=<SID>SquashArgument()<CR><CR>
5467 nnoremap <buffer> <silent> crn :<C-U>Grevert --no-commit <C-R>=<SID>SquashArgument()<CR><CR>
5468 nnoremap <buffer> <silent> cr? :help fugitive_cr<CR>
5470 nnoremap <buffer> cm<Space> :Git merge<Space>
5471 nnoremap <buffer> cm<CR> :Git merge<CR>
5472 nnoremap <buffer> <silent> cm? :help fugitive_cm<CR>
5474 nnoremap <buffer> cz<Space> :Git stash<Space>
5475 nnoremap <buffer> cz<CR> :Git stash<CR>
5476 nnoremap <buffer> <silent> cza :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5477 nnoremap <buffer> <silent> czA :<C-U>exe <SID>EchoExec(['stash', 'apply', '--quiet', 'stash@{' . v:count . '}'])<CR>
5478 nnoremap <buffer> <silent> czp :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', '--index', 'stash@{' . v:count . '}'])<CR>
5479 nnoremap <buffer> <silent> czP :<C-U>exe <SID>EchoExec(['stash', 'pop', '--quiet', 'stash@{' . v:count . '}'])<CR>
5480 nnoremap <buffer> <silent> czv :<C-U>exe 'Gedit' fugitive#RevParse('stash@{' . v:count . '}')<CR>
5481 nnoremap <buffer> <silent> czw :<C-U>exe <SID>EchoExec(['stash', '--keep-index'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5482 nnoremap <buffer> <silent> czz :<C-U>exe <SID>EchoExec(['stash'] + (v:count > 1 ? ['--all'] : v:count ? ['--include-untracked'] : []))<CR>
5483 nnoremap <buffer> <silent> cz? :<C-U>help fugitive_cz<CR>
5485 nnoremap <buffer> co<Space> :Git checkout<Space>
5486 nnoremap <buffer> co<CR> :Git checkout<CR>
5487 nnoremap <buffer> coo :exe <SID>EchoExec(['checkout'] + split(<SID>SquashArgument()) + ['--'])<CR>
5488 nnoremap <buffer> co? :<C-U>help fugitive_co<CR>
5490 nnoremap <buffer> cb<Space> :Git branch<Space>
5491 nnoremap <buffer> cb<CR> :Git branch<CR>
5492 nnoremap <buffer> cb? :<C-U>help fugitive_cb<CR>
5494 nnoremap <buffer> r<Space> :Git rebase<Space>
5495 nnoremap <buffer> r<CR> :Git rebase<CR>
5496 nnoremap <buffer> <silent> ri :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><CR>
5497 nnoremap <buffer> <silent> rf :<C-U>Grebase --autosquash<C-R>=<SID>RebaseArgument()<CR><CR>
5498 nnoremap <buffer> <silent> ru :<C-U>Grebase --interactive @{upstream}<CR>
5499 nnoremap <buffer> <silent> rp :<C-U>Grebase --interactive @{push}<CR>
5500 nnoremap <buffer> <silent> rw :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/reword/e<CR>
5501 nnoremap <buffer> <silent> rm :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/edit/e<CR>
5502 nnoremap <buffer> <silent> rd :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5503 nnoremap <buffer> <silent> rk :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5504 nnoremap <buffer> <silent> rx :<C-U>Grebase --interactive<C-R>=<SID>RebaseArgument()<CR><Bar>s/^pick/drop/e<CR>
5505 nnoremap <buffer> <silent> rr :<C-U>Grebase --continue<CR>
5506 nnoremap <buffer> <silent> rs :<C-U>Grebase --skip<CR>
5507 nnoremap <buffer> <silent> re :<C-U>Grebase --edit-todo<CR>
5508 nnoremap <buffer> <silent> ra :<C-U>Grebase --abort<CR>
5509 nnoremap <buffer> <silent> r? :<C-U>help fugitive_r<CR>
5511 call s:Map('n', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5512 call s:Map('x', '.', ":<C-U> <C-R>=<SID>fnameescape(fugitive#Real(@%))<CR><Home>")
5513 call s:Map('n', 'g?', ":<C-U>help fugitive-map<CR>", '<silent>')
5514 call s:Map('n', '<F1>', ":<C-U>help fugitive-map<CR>", '<silent>')
5518 function! s:StatusCfile(...) abort
5520 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5521 let info = s:StageInfo()
5522 let line = getline('.')
5523 if len(info.sigil) && len(info.section) && len(info.paths)
5524 if info.section ==# 'Unstaged' && info.sigil !=# '-'
5525 return [lead . info.relative[0], info.offset, 'normal!zv']
5526 elseif info.section ==# 'Staged' && info.sigil ==# '-'
5527 return ['@:' . info.relative[0], info.offset, 'normal!zv']
5529 return [':0:' . info.relative[0], info.offset, 'normal!zv']
5531 elseif len(info.paths)
5532 return [lead . info.relative[0]]
5533 elseif len(info.commit)
5534 return [info.commit]
5535 elseif line =~# '^\%(Head\|Merge\|Rebase\|Upstream\|Pull\|Push\): '
5536 return [matchstr(line, ' \zs.*')]
5542 function! fugitive#StatusCfile() abort
5543 let file = s:Generate(s:StatusCfile()[0])
5544 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5547 function! s:MessageCfile(...) abort
5549 let lead = s:cpath(tree, getcwd()) ? './' : tree . '/'
5550 if getline('.') =~# '^.\=\trenamed:.* -> '
5551 return lead . matchstr(getline('.'),' -> \zs.*')
5552 elseif getline('.') =~# '^.\=\t\(\k\| \)\+\p\?: *.'
5553 return lead . matchstr(getline('.'),': *\zs.\{-\}\ze\%( ([^()[:digit:]]\+)\)\=$')
5554 elseif getline('.') =~# '^.\=\t.'
5555 return lead . matchstr(getline('.'),'\t\zs.*')
5556 elseif getline('.') =~# ': needs merge$'
5557 return lead . matchstr(getline('.'),'.*\ze: needs merge$')
5558 elseif getline('.') =~# '^\%(. \)\=Not currently on any branch.$'
5560 elseif getline('.') =~# '^\%(. \)\=On branch '
5561 return 'refs/heads/'.getline('.')[12:]
5562 elseif getline('.') =~# "^\\%(. \\)\=Your branch .*'"
5563 return matchstr(getline('.'),"'\\zs\\S\\+\\ze'")
5569 function! fugitive#MessageCfile() abort
5570 let file = s:Generate(s:MessageCfile())
5571 return empty(file) ? fugitive#Cfile() : s:fnameescape(file)
5574 function! s:cfile() abort
5576 let myhash = s:DirRev(@%)[1]
5579 let myhash = fugitive#RevParse(myhash)
5584 if empty(myhash) && getline(1) =~# '^\%(commit\|tag\) \w'
5585 let myhash = matchstr(getline(1),'^\w\+ \zs\S\+')
5588 let showtree = (getline(1) =~# '^tree ' && getline(2) == "")
5590 let treebase = substitute(s:DirCommitFile(@%)[1], '^\d$', ':&', '') . ':' .
5591 \ s:Relative('') . (s:Relative('') =~# '^$\|/$' ? '' : '/')
5593 if getline('.') =~# '^\d\{6\} \l\{3,8\} \x\{40,\}\t'
5594 return [treebase . s:sub(matchstr(getline('.'),'\t\zs.*'),'/$','')]
5596 return [treebase . s:sub(getline('.'),'/$','')]
5603 if getline('.') =~# '^\d\{6\} \x\{40,\} \d\t'
5604 let ref = matchstr(getline('.'),'\x\{40,\}')
5605 let file = ':'.s:sub(matchstr(getline('.'),'\d\t.*'),'\t',':')
5609 if getline('.') =~# '^ref: '
5610 let ref = strpart(getline('.'),5)
5612 elseif getline('.') =~# '^commit \x\{40,\}\>'
5613 let ref = matchstr(getline('.'),'\x\{40,\}')
5616 elseif getline('.') =~# '^parent \x\{40,\}\>'
5617 let ref = matchstr(getline('.'),'\x\{40,\}')
5618 let line = line('.')
5620 while getline(line) =~# '^parent '
5626 elseif getline('.') =~# '^tree \x\{40,\}$'
5627 let ref = matchstr(getline('.'),'\x\{40,\}')
5628 if len(myhash) && fugitive#RevParse(myhash.':') ==# ref
5629 let ref = myhash.':'
5633 elseif getline('.') =~# '^object \x\{40,\}$' && getline(line('.')+1) =~ '^type \%(commit\|tree\|blob\)$'
5634 let ref = matchstr(getline('.'),'\x\{40,\}')
5635 let type = matchstr(getline(line('.')+1),'type \zs.*')
5637 elseif getline('.') =~# '^\l\{3,8\} '.myhash.'$'
5638 let ref = s:DirRev(@%)[1]
5640 elseif getline('.') =~# '^\l\{3,8\} \x\{40,\}\>'
5641 let ref = matchstr(getline('.'),'\x\{40,\}')
5642 echoerr "warning: unknown context ".matchstr(getline('.'),'^\l*')
5644 elseif getline('.') =~# '^[+-]\{3\} [abciow12]\=/'
5645 let ref = getline('.')[4:]
5647 elseif getline('.') =~# '^[+-]' && search('^@@ -\d\+\%(,\d\+\)\= +\d\+','bnW')
5648 let type = getline('.')[0]
5649 let lnum = line('.') - 1
5651 while getline(lnum) !~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5652 if getline(lnum) =~# '^[ '.type.']'
5657 let offset += matchstr(getline(lnum), type.'\zs\d\+')
5658 let ref = getline(search('^'.type.'\{3\} [abciow12]/','bnW'))[4:-1]
5659 let dcmds = [offset, 'normal!zv']
5661 elseif getline('.') =~# '^rename from '
5662 let ref = 'a/'.getline('.')[12:]
5663 elseif getline('.') =~# '^rename to '
5664 let ref = 'b/'.getline('.')[10:]
5666 elseif getline('.') =~# '^@@ -\d\+\%(,\d\+\)\= +\d\+'
5667 let diff = getline(search('^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)', 'bcnW'))
5668 let offset = matchstr(getline('.'), '+\zs\d\+')
5670 let dref = matchstr(diff, '\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5671 let ref = matchstr(diff, '\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5672 let dcmd = 'Gdiffsplit! +'.offset
5674 elseif getline('.') =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5675 let dref = matchstr(getline('.'),'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5676 let ref = matchstr(getline('.'),'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5677 let dcmd = 'Gdiffsplit!'
5679 elseif getline('.') =~# '^index ' && getline(line('.')-1) =~# '^diff --git \%([abciow12]/.*\|/dev/null\) \%([abciow12]/.*\|/dev/null\)'
5680 let line = getline(line('.')-1)
5681 let dref = matchstr(line,'\Cdiff --git \zs\%([abciow12]/.*\|/dev/null\)\ze \%([abciow12]/.*\|/dev/null\)')
5682 let ref = matchstr(line,'\Cdiff --git \%([abciow12]/.*\|/dev/null\) \zs\%([abciow12]/.*\|/dev/null\)')
5683 let dcmd = 'Gdiffsplit!'
5685 elseif line('$') == 1 && getline('.') =~ '^\x\{40,\}$'
5686 let ref = getline('.')
5688 elseif expand('<cword>') =~# '^\x\{7,\}\>'
5689 return [expand('<cword>')]
5704 let prefixes.a = myhash.'^:'
5705 let prefixes.b = myhash.':'
5707 let ref = substitute(ref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5709 let dref = substitute(dref, '^\(\w\)/', '\=get(prefixes, submatch(1), "HEAD:")', '')
5712 if ref ==# '/dev/null'
5714 let ref = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
5718 return [ref, dcmd . ' ' . s:fnameescape(dref)] + dcmds
5720 return [ref] + dcmds
5728 function! s:GF(mode) abort
5730 let results = &filetype ==# 'fugitive' ? s:StatusCfile() : &filetype ==# 'gitcommit' ? [s:MessageCfile()] : s:cfile()
5732 return 'echoerr ' . string(v:exception)
5735 return 'G' . a:mode .
5736 \ ' +' . escape(results[1], ' ') . ' ' .
5737 \ s:fnameescape(results[0]) . join(map(results[2:-1], '"|" . v:val'), '')
5738 elseif len(results) && len(results[0])
5739 return 'G' . a:mode . ' ' . s:fnameescape(results[0])
5745 function! fugitive#Cfile() abort
5747 let results = s:cfile()
5749 let cfile = expand('<cfile>')
5750 if &includeexpr =~# '\<v:fname\>'
5751 sandbox let cfile = eval(substitute(&includeexpr, '\C\<v:fname\>', '\=string(cfile)', 'g'))
5754 elseif len(results) > 1
5755 let pre = '+' . join(map(results[1:-1], 'escape(v:val, " ")'), '\|') . ' '
5757 return pre . s:fnameescape(s:Generate(results[0]))
5760 " Section: Statusline
5762 function! fugitive#Statusline(...) abort
5763 let dir = s:Dir(bufnr(''))
5768 let commit = s:DirCommitFile(@%)[1]
5770 let status .= ':' . commit[0:6]
5772 let status .= '('.FugitiveHead(7, dir).')'
5773 return '[Git'.status.']'
5776 function! fugitive#statusline(...) abort
5777 return fugitive#Statusline()
5780 function! fugitive#head(...) abort
5785 return fugitive#Head(a:0 ? a:1 : 0)
5790 function! fugitive#Foldtext() abort
5791 if &foldmethod !=# 'syntax'
5795 let line_foldstart = getline(v:foldstart)
5796 if line_foldstart =~# '^diff '
5797 let [add, remove] = [-1, -1]
5799 for lnum in range(v:foldstart, v:foldend)
5800 let line = getline(lnum)
5801 if filename ==# '' && line =~# '^[+-]\{3\} [abciow12]/'
5802 let filename = line[6:-1]
5806 elseif line =~# '^-'
5808 elseif line =~# '^Binary '
5813 let filename = matchstr(line_foldstart, '^diff .\{-\} [abciow12]/\zs.*\ze [abciow12]/')
5816 let filename = line_foldstart[5:-1]
5819 return 'Binary: '.filename
5821 return (add<10&&remove<100?' ':'') . add . '+ ' . (remove<10&&add<100?' ':'') . remove . '- ' . filename
5823 elseif line_foldstart =~# '^# .*:$'
5824 let lines = getline(v:foldstart, v:foldend)
5825 call filter(lines, 'v:val =~# "^#\t"')
5826 cal map(lines, "s:sub(v:val, '^#\t%(modified: +|renamed: +)=', '')")
5827 cal map(lines, "s:sub(v:val, '^([[:alpha:] ]+): +(.*)', '\\2 (\\1)')")
5828 return line_foldstart.' '.join(lines, ', ')
5833 function! fugitive#foldtext() abort
5834 return fugitive#Foldtext()
5837 augroup fugitive_folding
5839 autocmd User Fugitive
5840 \ if &filetype =~# '^git\%(commit\)\=$' && &foldtext ==# 'foldtext()' |
5841 \ set foldtext=fugitive#Foldtext() |
5845 " Section: Initialization
5847 function! fugitive#Init() abort
5848 if exists('#User#FugitiveBoot')
5850 let [save_mls, &modelines] = [&mls, 0]
5851 doautocmd User FugitiveBoot
5857 if stridx(&tags, escape(dir, ', ')) == -1 && &tags !~# '\.git' && !exists('s:tags_warning')
5858 let actualdir = fugitive#Find('.git/', dir)
5859 if filereadable(actualdir . 'tags')
5860 let s:tags_warning = 1
5862 echo "Fugitive .git/tags support removed in favor of `:set tags^=./.git/tags;`"
5867 let [save_mls, &modelines] = [&mls, 0]
5868 call s:define_commands()
5869 doautocmd User Fugitive
5875 function! fugitive#is_git_dir(path) abort
5876 return FugitiveIsGitDir(a:path)
5879 function! fugitive#extract_git_dir(path) abort
5880 return FugitiveExtractGitDir(a:path)
5883 function! fugitive#detect(path) abort
5884 return FugitiveDetect(a:path)